| 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 459 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 470 if (!ComputePropertyAccessInfo(map, name, access_mode, &access_info)) { | 470 if (!ComputePropertyAccessInfo(map, name, access_mode, &access_info)) { |
| 471 return false; | 471 return false; |
| 472 } | 472 } |
| 473 access_infos->push_back(access_info); | 473 access_infos->push_back(access_info); |
| 474 } | 474 } |
| 475 } | 475 } |
| 476 return true; | 476 return true; |
| 477 } | 477 } |
| 478 | 478 |
| 479 | 479 |
| 480 Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { | 480 Reduction JSNativeContextSpecialization::ReduceNamedAccess( |
| 481 DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode()); | 481 Node* node, Node* value, MapHandleList const& receiver_maps, |
| 482 NamedAccess const& p = NamedAccessOf(node->op()); | 482 Handle<Name> name, PropertyAccessMode access_mode) { |
| 483 Handle<Name> name = p.name(); | 483 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || |
| 484 node->opcode() == IrOpcode::kJSStoreNamed); |
| 484 Node* receiver = NodeProperties::GetValueInput(node, 0); | 485 Node* receiver = NodeProperties::GetValueInput(node, 0); |
| 485 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); | 486 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); |
| 486 Node* effect = NodeProperties::GetEffectInput(node); | 487 Node* effect = NodeProperties::GetEffectInput(node); |
| 487 Node* control = NodeProperties::GetControlInput(node); | 488 Node* control = NodeProperties::GetControlInput(node); |
| 488 | 489 |
| 489 // Not much we can do if deoptimization support is disabled. | 490 // Not much we can do if deoptimization support is disabled. |
| 490 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); | 491 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); |
| 491 | 492 |
| 492 // Extract receiver maps from the LOAD_IC using the LoadICNexus. | |
| 493 MapHandleList receiver_maps; | |
| 494 if (!p.feedback().IsValid()) return NoChange(); | |
| 495 LoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); | |
| 496 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); | |
| 497 DCHECK_LT(0, receiver_maps.length()); | |
| 498 | |
| 499 // Compute property access infos for the receiver maps. | 493 // Compute property access infos for the receiver maps. |
| 500 ZoneVector<PropertyAccessInfo> access_infos(zone()); | 494 ZoneVector<PropertyAccessInfo> access_infos(zone()); |
| 501 if (!ComputePropertyAccessInfos(receiver_maps, name, kLoad, &access_infos)) { | 495 if (!ComputePropertyAccessInfos(receiver_maps, name, access_mode, |
| 496 &access_infos)) { |
| 502 return NoChange(); | 497 return NoChange(); |
| 503 } | 498 } |
| 504 | 499 |
| 505 // Nothing to do if we have no non-deprecated maps. | 500 // Nothing to do if we have no non-deprecated maps. |
| 506 if (access_infos.empty()) return NoChange(); | 501 if (access_infos.empty()) return NoChange(); |
| 507 | 502 |
| 508 // The final states for every polymorphic branch. We join them with | 503 // The final states for every polymorphic branch. We join them with |
| 509 // Merge+Phi+EffectPhi at the bottom. | 504 // Merge++Phi+EffectPhi at the bottom. |
| 510 ZoneVector<Node*> values(zone()); | 505 ZoneVector<Node*> values(zone()); |
| 511 ZoneVector<Node*> effects(zone()); | 506 ZoneVector<Node*> effects(zone()); |
| 512 ZoneVector<Node*> controls(zone()); | 507 ZoneVector<Node*> controls(zone()); |
| 513 | 508 |
| 514 // The list of "exiting" controls, which currently go to a single deoptimize. | 509 // The list of "exiting" controls, which currently go to a single deoptimize. |
| 515 // TODO(bmeurer): Consider using an IC as fallback. | 510 // TODO(bmeurer): Consider using an IC as fallback. |
| 516 Node* const exit_effect = effect; | 511 Node* const exit_effect = effect; |
| 517 ZoneVector<Node*> exit_controls(zone()); | 512 ZoneVector<Node*> exit_controls(zone()); |
| 518 | 513 |
| 519 // Ensure that {receiver} is a heap object. | 514 // Ensure that {receiver} is a heap object. |
| 520 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); | 515 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); |
| 521 Node* branch = | 516 Node* branch = |
| 522 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); | 517 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); |
| 523 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | 518 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); |
| 524 control = graph()->NewNode(common()->IfFalse(), branch); | 519 control = graph()->NewNode(common()->IfFalse(), branch); |
| 525 | 520 |
| 526 // Load the {receiver} map. The resulting effect is the dominating effect for | 521 // Load the {receiver} map. The resulting effect is the dominating effect for |
| 527 // all (polymorphic) branches. | 522 // all (polymorphic) branches. |
| 528 Node* receiver_map = effect = | 523 Node* receiver_map = effect = |
| 529 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | 524 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
| 530 receiver, effect, control); | 525 receiver, effect, control); |
| 531 | 526 |
| 532 // Generate code for the various different property access patterns. | 527 // Generate code for the various different property access patterns. |
| 533 Node* fallthrough_control = control; | 528 Node* fallthrough_control = control; |
| 534 for (PropertyAccessInfo const& access_info : access_infos) { | 529 for (PropertyAccessInfo const& access_info : access_infos) { |
| 535 Node* this_value = receiver; | 530 Node* this_value = value; |
| 531 Node* this_receiver = receiver; |
| 536 Node* this_effect = effect; | 532 Node* this_effect = effect; |
| 537 Node* this_control; | 533 Node* this_control; |
| 538 | 534 |
| 539 // Perform map check on {receiver}. | 535 // Perform map check on {receiver}. |
| 540 Type* receiver_type = access_info.receiver_type(); | 536 Type* receiver_type = access_info.receiver_type(); |
| 541 if (receiver_type->Is(Type::String())) { | 537 if (receiver_type->Is(Type::String())) { |
| 542 // Emit an instance type check for strings. | 538 // Emit an instance type check for strings. |
| 543 Node* receiver_instance_type = this_effect = graph()->NewNode( | 539 Node* receiver_instance_type = this_effect = graph()->NewNode( |
| 544 simplified()->LoadField(AccessBuilder::ForMapInstanceType()), | 540 simplified()->LoadField(AccessBuilder::ForMapInstanceType()), |
| 545 receiver_map, this_effect, fallthrough_control); | 541 receiver_map, this_effect, fallthrough_control); |
| 546 Node* check = | 542 Node* check = |
| 547 graph()->NewNode(machine()->Uint32LessThan(), receiver_instance_type, | 543 graph()->NewNode(machine()->Uint32LessThan(), receiver_instance_type, |
| 548 jsgraph()->Uint32Constant(FIRST_NONSTRING_TYPE)); | 544 jsgraph()->Uint32Constant(FIRST_NONSTRING_TYPE)); |
| 549 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 545 Node* branch = |
| 550 check, fallthrough_control); | 546 graph()->NewNode(common()->Branch(), check, fallthrough_control); |
| 551 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | 547 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); |
| 552 this_control = graph()->NewNode(common()->IfTrue(), branch); | 548 this_control = graph()->NewNode(common()->IfTrue(), branch); |
| 553 } else { | 549 } else { |
| 554 // Emit a (sequence of) map checks for other properties. | 550 // Emit a (sequence of) map checks for other properties. |
| 555 ZoneVector<Node*> this_controls(zone()); | 551 ZoneVector<Node*> this_controls(zone()); |
| 556 for (auto i = access_info.receiver_type()->Classes(); !i.Done(); | 552 for (auto i = access_info.receiver_type()->Classes(); !i.Done(); |
| 557 i.Advance()) { | 553 i.Advance()) { |
| 558 Handle<Map> map = i.Current(); | 554 Handle<Map> map = i.Current(); |
| 559 Node* check = | 555 Node* check = |
| 560 graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), | 556 graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), |
| 561 receiver_map, jsgraph()->Constant(map)); | 557 receiver_map, jsgraph()->Constant(map)); |
| 562 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 558 Node* branch = |
| 563 check, fallthrough_control); | 559 graph()->NewNode(common()->Branch(), check, fallthrough_control); |
| 564 this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | 560 this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); |
| 565 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | 561 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); |
| 566 } | 562 } |
| 567 int const this_control_count = static_cast<int>(this_controls.size()); | 563 int const this_control_count = static_cast<int>(this_controls.size()); |
| 568 this_control = | 564 this_control = |
| 569 (this_control_count == 1) | 565 (this_control_count == 1) |
| 570 ? this_controls.front() | 566 ? this_controls.front() |
| 571 : graph()->NewNode(common()->Merge(this_control_count), | 567 : graph()->NewNode(common()->Merge(this_control_count), |
| 572 this_control_count, &this_controls.front()); | 568 this_control_count, &this_controls.front()); |
| 573 } | 569 } |
| 574 | 570 |
| 575 // Determine actual holder and perform prototype chain checks. | 571 // Determine actual holder and perform prototype chain checks. |
| 576 Handle<JSObject> holder; | 572 Handle<JSObject> holder; |
| 577 if (access_info.holder().ToHandle(&holder)) { | 573 if (access_info.holder().ToHandle(&holder)) { |
| 578 this_value = jsgraph()->Constant(holder); | |
| 579 AssumePrototypesStable(receiver_type, holder); | 574 AssumePrototypesStable(receiver_type, holder); |
| 575 if (access_mode == kLoad) { |
| 576 this_receiver = jsgraph()->Constant(holder); |
| 577 } |
| 580 } | 578 } |
| 581 | 579 |
| 582 // Generate the actual property access. | 580 // Generate the actual property access. |
| 583 if (access_info.IsDataConstant()) { | 581 if (access_info.IsDataConstant()) { |
| 584 this_value = jsgraph()->Constant(access_info.constant()); | 582 this_value = jsgraph()->Constant(access_info.constant()); |
| 583 if (access_mode == kStore) { |
| 584 Node* check = graph()->NewNode( |
| 585 simplified()->ReferenceEqual(Type::Tagged()), value, this_value); |
| 586 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
| 587 check, this_control); |
| 588 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); |
| 589 this_control = graph()->NewNode(common()->IfTrue(), branch); |
| 590 } |
| 585 } else { | 591 } else { |
| 586 // TODO(bmeurer): This is sort of adhoc, and must be refactored into some | |
| 587 // common code once we also have support for stores. | |
| 588 DCHECK(access_info.IsDataField()); | 592 DCHECK(access_info.IsDataField()); |
| 589 FieldIndex const field_index = access_info.field_index(); | 593 FieldIndex const field_index = access_info.field_index(); |
| 590 Type* const field_type = access_info.field_type(); | 594 Type* const field_type = access_info.field_type(); |
| 591 if (!field_index.is_inobject()) { | 595 if (!field_index.is_inobject()) { |
| 592 this_value = this_effect = graph()->NewNode( | 596 this_receiver = this_effect = graph()->NewNode( |
| 593 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), | 597 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), |
| 594 this_value, this_effect, this_control); | 598 this_receiver, this_effect, this_control); |
| 595 } | 599 } |
| 596 FieldAccess field_access; | 600 FieldAccess field_access = {kTaggedBase, field_index.offset(), name, |
| 597 field_access.base_is_tagged = kTaggedBase; | 601 field_type, kMachAnyTagged}; |
| 598 field_access.offset = field_index.offset(); | 602 if (access_mode == kLoad) { |
| 599 field_access.name = name; | 603 if (field_type->Is(Type::UntaggedFloat64())) { |
| 600 field_access.type = field_type; | 604 if (!field_index.is_inobject() || field_index.is_hidden_field() || |
| 601 field_access.machine_type = kMachAnyTagged; | 605 !FLAG_unbox_double_fields) { |
| 602 if (field_type->Is(Type::UntaggedFloat64())) { | 606 this_receiver = this_effect = |
| 603 if (!field_index.is_inobject() || field_index.is_hidden_field() || | 607 graph()->NewNode(simplified()->LoadField(field_access), |
| 604 !FLAG_unbox_double_fields) { | 608 this_receiver, this_effect, this_control); |
| 605 this_value = this_effect = | 609 field_access.offset = HeapNumber::kValueOffset; |
| 606 graph()->NewNode(simplified()->LoadField(field_access), | 610 field_access.name = MaybeHandle<Name>(); |
| 607 this_value, this_effect, this_control); | 611 } |
| 608 field_access.offset = HeapNumber::kValueOffset; | 612 field_access.machine_type = kMachFloat64; |
| 609 field_access.name = MaybeHandle<Name>(); | |
| 610 } | 613 } |
| 611 field_access.machine_type = kMachFloat64; | 614 this_value = this_effect = |
| 615 graph()->NewNode(simplified()->LoadField(field_access), |
| 616 this_receiver, this_effect, this_control); |
| 617 } else { |
| 618 DCHECK_EQ(kStore, access_mode); |
| 619 if (field_type->Is(Type::TaggedSigned())) { |
| 620 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); |
| 621 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
| 622 check, this_control); |
| 623 exit_controls.push_back( |
| 624 graph()->NewNode(common()->IfFalse(), branch)); |
| 625 this_control = graph()->NewNode(common()->IfTrue(), branch); |
| 626 } else if (field_type->Is(Type::TaggedPointer())) { |
| 627 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); |
| 628 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
| 629 check, this_control); |
| 630 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); |
| 631 this_control = graph()->NewNode(common()->IfFalse(), branch); |
| 632 if (field_type->NumClasses() > 0) { |
| 633 // Emit a (sequence of) map checks for the value. |
| 634 ZoneVector<Node*> this_controls(zone()); |
| 635 Node* value_map = this_effect = graph()->NewNode( |
| 636 simplified()->LoadField(AccessBuilder::ForMap()), value, |
| 637 this_effect, this_control); |
| 638 for (auto i = field_type->Classes(); !i.Done(); i.Advance()) { |
| 639 Handle<Map> field_map(i.Current()); |
| 640 check = graph()->NewNode( |
| 641 simplified()->ReferenceEqual(Type::Internal()), value_map, |
| 642 jsgraph()->Constant(field_map)); |
| 643 branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
| 644 check, this_control); |
| 645 this_control = graph()->NewNode(common()->IfFalse(), branch); |
| 646 this_controls.push_back( |
| 647 graph()->NewNode(common()->IfTrue(), branch)); |
| 648 } |
| 649 exit_controls.push_back(this_control); |
| 650 int const this_control_count = |
| 651 static_cast<int>(this_controls.size()); |
| 652 this_control = |
| 653 (this_control_count == 1) |
| 654 ? this_controls.front() |
| 655 : graph()->NewNode(common()->Merge(this_control_count), |
| 656 this_control_count, |
| 657 &this_controls.front()); |
| 658 } |
| 659 } else { |
| 660 DCHECK(field_type->Is(Type::Tagged())); |
| 661 } |
| 662 this_effect = graph()->NewNode(simplified()->StoreField(field_access), |
| 663 this_receiver, this_value, this_effect, |
| 664 this_control); |
| 612 } | 665 } |
| 613 this_value = this_effect = | |
| 614 graph()->NewNode(simplified()->LoadField(field_access), this_value, | |
| 615 this_effect, this_control); | |
| 616 } | 666 } |
| 617 | 667 |
| 618 // Remember the final state for this property access. | 668 // Remember the final state for this property access. |
| 619 values.push_back(this_value); | 669 values.push_back(this_value); |
| 620 effects.push_back(this_effect); | 670 effects.push_back(this_effect); |
| 621 controls.push_back(this_control); | 671 controls.push_back(this_control); |
| 622 } | 672 } |
| 623 | 673 |
| 624 // Collect the fallthru control as final "exit" control. | 674 // Collect the fallthru control as final "exit" control. |
| 675 if (fallthrough_control != control) { |
| 676 // Mark the last fallthru branch as deferred. |
| 677 Node* branch = NodeProperties::GetControlInput(fallthrough_control); |
| 678 DCHECK_EQ(IrOpcode::kBranch, branch->opcode()); |
| 679 if (fallthrough_control->opcode() == IrOpcode::kIfTrue) { |
| 680 NodeProperties::ChangeOp(branch, common()->Branch(BranchHint::kFalse)); |
| 681 } else { |
| 682 DCHECK_EQ(IrOpcode::kIfFalse, fallthrough_control->opcode()); |
| 683 NodeProperties::ChangeOp(branch, common()->Branch(BranchHint::kTrue)); |
| 684 } |
| 685 } |
| 625 exit_controls.push_back(fallthrough_control); | 686 exit_controls.push_back(fallthrough_control); |
| 626 | 687 |
| 627 // Generate the single "exit" point, where we get if either all map/instance | 688 // Generate the single "exit" point, where we get if either all map/instance |
| 628 // type checks failed, or one of the assumptions inside one of the cases | 689 // type checks failed, or one of the assumptions inside one of the cases |
| 629 // failes (i.e. failing prototype chain check). | 690 // failes (i.e. failing prototype chain check). |
| 630 // TODO(bmeurer): Consider falling back to IC here if deoptimization is | 691 // TODO(bmeurer): Consider falling back to IC here if deoptimization is |
| 631 // disabled. | 692 // disabled. |
| 632 int const exit_control_count = static_cast<int>(exit_controls.size()); | 693 int const exit_control_count = static_cast<int>(exit_controls.size()); |
| 633 Node* exit_control = | 694 Node* exit_control = |
| 634 (exit_control_count == 1) | 695 (exit_control_count == 1) |
| 635 ? exit_controls.front() | 696 ? exit_controls.front() |
| 636 : graph()->NewNode(common()->Merge(exit_control_count), | 697 : graph()->NewNode(common()->Merge(exit_control_count), |
| 637 exit_control_count, &exit_controls.front()); | 698 exit_control_count, &exit_controls.front()); |
| 638 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, | 699 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, |
| 639 exit_effect, exit_control); | 700 exit_effect, exit_control); |
| 640 // TODO(bmeurer): This should be on the AdvancedReducer somehow. | 701 // TODO(bmeurer): This should be on the AdvancedReducer somehow. |
| 641 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); | 702 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); |
| 642 | 703 |
| 643 // Generate the final merge point for all (polymorphic) branches. | 704 // Generate the final merge point for all (polymorphic) branches. |
| 644 Node* value; | |
| 645 int const control_count = static_cast<int>(controls.size()); | 705 int const control_count = static_cast<int>(controls.size()); |
| 646 if (control_count == 1) { | 706 if (control_count == 1) { |
| 647 value = values.front(); | 707 value = values.front(); |
| 648 effect = effects.front(); | 708 effect = effects.front(); |
| 649 control = controls.front(); | 709 control = controls.front(); |
| 650 } else { | 710 } else { |
| 651 control = graph()->NewNode(common()->Merge(control_count), control_count, | 711 control = graph()->NewNode(common()->Merge(control_count), control_count, |
| 652 &controls.front()); | 712 &controls.front()); |
| 653 values.push_back(control); | 713 values.push_back(control); |
| 654 value = graph()->NewNode(common()->Phi(kMachAnyTagged, control_count), | 714 value = graph()->NewNode(common()->Phi(kMachAnyTagged, control_count), |
| 655 control_count + 1, &values.front()); | 715 control_count + 1, &values.front()); |
| 656 effects.push_back(control); | 716 effects.push_back(control); |
| 657 effect = graph()->NewNode(common()->EffectPhi(control_count), | 717 effect = graph()->NewNode(common()->EffectPhi(control_count), |
| 658 control_count + 1, &effects.front()); | 718 control_count + 1, &effects.front()); |
| 659 } | 719 } |
| 660 return Replace(node, value, effect, control); | 720 return Replace(node, value, effect, control); |
| 661 } | 721 } |
| 662 | 722 |
| 663 | 723 |
| 724 Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { |
| 725 DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode()); |
| 726 NamedAccess const& p = NamedAccessOf(node->op()); |
| 727 Node* const value = jsgraph()->Dead(); |
| 728 |
| 729 // Extract receiver maps from the LOAD_IC using the LoadICNexus. |
| 730 MapHandleList receiver_maps; |
| 731 if (!p.feedback().IsValid()) return NoChange(); |
| 732 LoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
| 733 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); |
| 734 DCHECK_LT(0, receiver_maps.length()); |
| 735 |
| 736 // Try to lower the named access based on the {receiver_maps}. |
| 737 return ReduceNamedAccess(node, value, receiver_maps, p.name(), kLoad); |
| 738 } |
| 739 |
| 740 |
| 664 Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) { | 741 Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) { |
| 665 DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode()); | 742 DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode()); |
| 666 NamedAccess const& p = NamedAccessOf(node->op()); | 743 NamedAccess const& p = NamedAccessOf(node->op()); |
| 667 Handle<Name> name = p.name(); | 744 Node* const value = NodeProperties::GetValueInput(node, 1); |
| 668 Node* receiver = NodeProperties::GetValueInput(node, 0); | |
| 669 Node* value = NodeProperties::GetValueInput(node, 1); | |
| 670 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); | |
| 671 Node* effect = NodeProperties::GetEffectInput(node); | |
| 672 Node* control = NodeProperties::GetControlInput(node); | |
| 673 | |
| 674 // Not much we can do if deoptimization support is disabled. | |
| 675 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); | |
| 676 | 745 |
| 677 // Extract receiver maps from the STORE_IC using the StoreICNexus. | 746 // Extract receiver maps from the STORE_IC using the StoreICNexus. |
| 678 MapHandleList receiver_maps; | 747 MapHandleList receiver_maps; |
| 679 if (!p.feedback().IsValid()) return NoChange(); | 748 if (!p.feedback().IsValid()) return NoChange(); |
| 680 StoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); | 749 StoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
| 681 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); | 750 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); |
| 682 DCHECK_LT(0, receiver_maps.length()); | 751 DCHECK_LT(0, receiver_maps.length()); |
| 683 | 752 |
| 684 // Compute property access infos for the receiver maps. | 753 // Try to lower the named access based on the {receiver_maps}. |
| 685 ZoneVector<PropertyAccessInfo> access_infos(zone()); | 754 return ReduceNamedAccess(node, value, receiver_maps, p.name(), kStore); |
| 686 if (!ComputePropertyAccessInfos(receiver_maps, name, kStore, &access_infos)) { | |
| 687 return NoChange(); | |
| 688 } | |
| 689 | |
| 690 // Nothing to do if we have no non-deprecated maps. | |
| 691 if (access_infos.empty()) return NoChange(); | |
| 692 | |
| 693 // The final states for every polymorphic branch. We join them with | |
| 694 // Merge+EffectPhi at the bottom. | |
| 695 ZoneVector<Node*> effects(zone()); | |
| 696 ZoneVector<Node*> controls(zone()); | |
| 697 | |
| 698 // The list of "exiting" controls, which currently go to a single deoptimize. | |
| 699 // TODO(bmeurer): Consider using an IC as fallback. | |
| 700 Node* const exit_effect = effect; | |
| 701 ZoneVector<Node*> exit_controls(zone()); | |
| 702 | |
| 703 // Ensure that {receiver} is a heap object. | |
| 704 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); | |
| 705 Node* branch = | |
| 706 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); | |
| 707 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | |
| 708 control = graph()->NewNode(common()->IfFalse(), branch); | |
| 709 | |
| 710 // Load the {receiver} map. The resulting effect is the dominating effect for | |
| 711 // all (polymorphic) branches. | |
| 712 Node* receiver_map = effect = | |
| 713 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | |
| 714 receiver, effect, control); | |
| 715 | |
| 716 // Generate code for the various different property access patterns. | |
| 717 Node* fallthrough_control = control; | |
| 718 for (PropertyAccessInfo const& access_info : access_infos) { | |
| 719 Node* this_receiver = receiver; | |
| 720 Node* this_effect = effect; | |
| 721 Node* this_control; | |
| 722 | |
| 723 // Perform map check on {receiver}. | |
| 724 Type* receiver_type = access_info.receiver_type(); | |
| 725 if (receiver_type->Is(Type::String())) { | |
| 726 // Emit an instance type check for strings. | |
| 727 Node* receiver_instance_type = this_effect = graph()->NewNode( | |
| 728 simplified()->LoadField(AccessBuilder::ForMapInstanceType()), | |
| 729 receiver_map, this_effect, fallthrough_control); | |
| 730 Node* check = | |
| 731 graph()->NewNode(machine()->Uint32LessThan(), receiver_instance_type, | |
| 732 jsgraph()->Uint32Constant(FIRST_NONSTRING_TYPE)); | |
| 733 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
| 734 check, fallthrough_control); | |
| 735 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | |
| 736 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
| 737 } else { | |
| 738 // Emit a (sequence of) map checks for other properties. | |
| 739 ZoneVector<Node*> this_controls(zone()); | |
| 740 for (auto i = access_info.receiver_type()->Classes(); !i.Done(); | |
| 741 i.Advance()) { | |
| 742 Handle<Map> map = i.Current(); | |
| 743 Node* check = | |
| 744 graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), | |
| 745 receiver_map, jsgraph()->Constant(map)); | |
| 746 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
| 747 check, fallthrough_control); | |
| 748 this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | |
| 749 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | |
| 750 } | |
| 751 int const this_control_count = static_cast<int>(this_controls.size()); | |
| 752 this_control = | |
| 753 (this_control_count == 1) | |
| 754 ? this_controls.front() | |
| 755 : graph()->NewNode(common()->Merge(this_control_count), | |
| 756 this_control_count, &this_controls.front()); | |
| 757 } | |
| 758 | |
| 759 // Determine actual holder and perform prototype chain checks. | |
| 760 Handle<JSObject> holder; | |
| 761 if (access_info.holder().ToHandle(&holder)) { | |
| 762 AssumePrototypesStable(receiver_type, holder); | |
| 763 } | |
| 764 | |
| 765 // Generate the actual property access. | |
| 766 if (access_info.IsDataConstant()) { | |
| 767 Node* check = | |
| 768 graph()->NewNode(simplified()->ReferenceEqual(Type::Tagged()), value, | |
| 769 jsgraph()->Constant(access_info.constant())); | |
| 770 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
| 771 check, this_control); | |
| 772 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); | |
| 773 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
| 774 } else { | |
| 775 // TODO(bmeurer): This is sort of adhoc, and must be refactored into some | |
| 776 // common code once we also have support for stores. | |
| 777 DCHECK(access_info.IsDataField()); | |
| 778 FieldIndex const field_index = access_info.field_index(); | |
| 779 Type* const field_type = access_info.field_type(); | |
| 780 if (!field_index.is_inobject()) { | |
| 781 this_receiver = this_effect = graph()->NewNode( | |
| 782 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), | |
| 783 this_receiver, this_effect, this_control); | |
| 784 } | |
| 785 FieldAccess field_access; | |
| 786 field_access.base_is_tagged = kTaggedBase; | |
| 787 field_access.offset = field_index.offset(); | |
| 788 field_access.name = name; | |
| 789 field_access.type = field_type; | |
| 790 field_access.machine_type = kMachAnyTagged; | |
| 791 if (field_type->Is(Type::TaggedSigned())) { | |
| 792 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); | |
| 793 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
| 794 check, this_control); | |
| 795 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); | |
| 796 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
| 797 } else if (field_type->Is(Type::TaggedPointer())) { | |
| 798 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); | |
| 799 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), | |
| 800 check, this_control); | |
| 801 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | |
| 802 this_control = graph()->NewNode(common()->IfFalse(), branch); | |
| 803 if (field_type->NumClasses() > 0) { | |
| 804 // Emit a (sequence of) map checks for the value. | |
| 805 ZoneVector<Node*> this_controls(zone()); | |
| 806 Node* value_map = this_effect = | |
| 807 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | |
| 808 value, this_effect, this_control); | |
| 809 for (auto i = field_type->Classes(); !i.Done(); i.Advance()) { | |
| 810 Handle<Map> field_map(i.Current()); | |
| 811 check = | |
| 812 graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), | |
| 813 value_map, jsgraph()->Constant(field_map)); | |
| 814 branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
| 815 check, this_control); | |
| 816 this_control = graph()->NewNode(common()->IfFalse(), branch); | |
| 817 this_controls.push_back( | |
| 818 graph()->NewNode(common()->IfTrue(), branch)); | |
| 819 } | |
| 820 exit_controls.push_back(this_control); | |
| 821 int const this_control_count = static_cast<int>(this_controls.size()); | |
| 822 this_control = (this_control_count == 1) | |
| 823 ? this_controls.front() | |
| 824 : graph()->NewNode( | |
| 825 common()->Merge(this_control_count), | |
| 826 this_control_count, &this_controls.front()); | |
| 827 } | |
| 828 } else { | |
| 829 DCHECK(field_type->Is(Type::Tagged())); | |
| 830 } | |
| 831 this_effect = | |
| 832 graph()->NewNode(simplified()->StoreField(field_access), | |
| 833 this_receiver, value, this_effect, this_control); | |
| 834 } | |
| 835 | |
| 836 // Remember the final state for this property access. | |
| 837 effects.push_back(this_effect); | |
| 838 controls.push_back(this_control); | |
| 839 } | |
| 840 | |
| 841 // Collect the fallthru control as final "exit" control. | |
| 842 exit_controls.push_back(fallthrough_control); | |
| 843 | |
| 844 // Generate the single "exit" point, where we get if either all map/instance | |
| 845 // type checks failed, or one of the assumptions inside one of the cases | |
| 846 // failes (i.e. failing prototype chain check). | |
| 847 // TODO(bmeurer): Consider falling back to IC here if deoptimization is | |
| 848 // disabled. | |
| 849 int const exit_control_count = static_cast<int>(exit_controls.size()); | |
| 850 Node* exit_control = | |
| 851 (exit_control_count == 1) | |
| 852 ? exit_controls.front() | |
| 853 : graph()->NewNode(common()->Merge(exit_control_count), | |
| 854 exit_control_count, &exit_controls.front()); | |
| 855 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, | |
| 856 exit_effect, exit_control); | |
| 857 // TODO(bmeurer): This should be on the AdvancedReducer somehow. | |
| 858 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); | |
| 859 | |
| 860 // Generate the final merge point for all (polymorphic) branches. | |
| 861 int const control_count = static_cast<int>(controls.size()); | |
| 862 if (control_count == 1) { | |
| 863 effect = effects.front(); | |
| 864 control = controls.front(); | |
| 865 } else { | |
| 866 control = graph()->NewNode(common()->Merge(control_count), control_count, | |
| 867 &controls.front()); | |
| 868 effects.push_back(control); | |
| 869 effect = graph()->NewNode(common()->EffectPhi(control_count), | |
| 870 control_count + 1, &effects.front()); | |
| 871 } | |
| 872 return Replace(node, value, effect, control); | |
| 873 } | 755 } |
| 874 | 756 |
| 875 | 757 |
| 876 Reduction JSNativeContextSpecialization::Replace(Node* node, | 758 Reduction JSNativeContextSpecialization::Replace(Node* node, |
| 877 Handle<Object> value) { | 759 Handle<Object> value) { |
| 878 return Replace(node, jsgraph()->Constant(value)); | 760 return Replace(node, jsgraph()->Constant(value)); |
| 879 } | 761 } |
| 880 | 762 |
| 881 | 763 |
| 882 bool JSNativeContextSpecialization::LookupInScriptContextTable( | 764 bool JSNativeContextSpecialization::LookupInScriptContextTable( |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 954 } | 836 } |
| 955 | 837 |
| 956 | 838 |
| 957 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { | 839 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { |
| 958 return jsgraph()->simplified(); | 840 return jsgraph()->simplified(); |
| 959 } | 841 } |
| 960 | 842 |
| 961 } // namespace compiler | 843 } // namespace compiler |
| 962 } // namespace internal | 844 } // namespace internal |
| 963 } // namespace v8 | 845 } // namespace v8 |
| OLD | NEW |