Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/flow_graph_optimizer.h" | 5 #include "vm/flow_graph_optimizer.h" |
| 6 | 6 |
| 7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
| 8 #include "vm/cha.h" | 8 #include "vm/cha.h" |
| 9 #include "vm/flow_graph_builder.h" | 9 #include "vm/flow_graph_builder.h" |
| 10 #include "vm/hash_map.h" | 10 #include "vm/hash_map.h" |
| (...skipping 608 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 619 ZoneGrowableArray<Function*>* overriding_functions = | 619 ZoneGrowableArray<Function*>* overriding_functions = |
| 620 CHA::GetNamedInstanceFunctionsOf(*subclass_cids, call->function_name()); | 620 CHA::GetNamedInstanceFunctionsOf(*subclass_cids, call->function_name()); |
| 621 if (overriding_functions->is_empty()) { | 621 if (overriding_functions->is_empty()) { |
| 622 // No overriding functions. | 622 // No overriding functions. |
| 623 return false; | 623 return false; |
| 624 } | 624 } |
| 625 } | 625 } |
| 626 return true; | 626 return true; |
| 627 } | 627 } |
| 628 | 628 |
| 629 | |
| 630 void FlowGraphOptimizer::InlineImplicitInstanceGetter(InstanceCallInstr* call) { | |
| 631 ASSERT(call->HasICData()); | |
| 632 const ICData& ic_data = *call->ic_data(); | |
| 633 Function& target = Function::Handle(); | |
| 634 GrowableArray<intptr_t> class_ids; | |
| 635 ic_data.GetCheckAt(0, &class_ids, &target); | |
| 636 ASSERT(class_ids.length() == 1); | |
| 637 // Inline implicit instance getter. | |
| 638 const String& field_name = | |
| 639 String::Handle(Field::NameFromGetter(call->function_name())); | |
| 640 const Field& field = Field::Handle(GetField(class_ids[0], field_name)); | |
| 641 ASSERT(!field.IsNull()); | |
| 642 | |
| 643 if (InstanceCallNeedsClassCheck(call)) { | |
| 644 AddCheckClass(call, call->ArgumentAt(0)->value()->Copy()); | |
| 645 } | |
| 646 // Detach environment from the original instruction because it can't | |
| 647 // deoptimize. | |
| 648 call->set_env(NULL); | |
| 649 LoadFieldInstr* load = new LoadFieldInstr( | |
| 650 call->ArgumentAt(0)->value(), | |
| 651 field.Offset(), | |
| 652 AbstractType::ZoneHandle(field.type())); | |
| 653 call->ReplaceWith(load, current_iterator()); | |
| 654 RemovePushArguments(call); | |
| 655 } | |
| 656 | |
| 657 | |
| 658 void FlowGraphOptimizer::InlineArrayLengthGetter(InstanceCallInstr* call, | |
| 659 intptr_t length_offset, | |
| 660 bool is_immutable) { | |
| 661 // Check receiver class. | |
| 662 AddCheckClass(call, call->ArgumentAt(0)->value()->Copy()); | |
| 663 | |
| 664 LoadFieldInstr* load = new LoadFieldInstr( | |
| 665 call->ArgumentAt(0)->value(), | |
| 666 length_offset, | |
| 667 Type::ZoneHandle(Type::SmiType()), | |
| 668 is_immutable); | |
| 669 load->set_result_cid(kSmiCid); | |
| 670 call->ReplaceWith(load, current_iterator()); | |
| 671 RemovePushArguments(call); | |
| 672 } | |
| 673 | |
| 674 | |
| 675 void FlowGraphOptimizer::InlineGArrayCapacityGetter(InstanceCallInstr* call) { | |
| 676 // Check receiver class. | |
| 677 AddCheckClass(call, call->ArgumentAt(0)->value()->Copy()); | |
| 678 | |
| 679 // TODO(srdjan): type of load should be GrowableObjectArrayType. | |
| 680 LoadFieldInstr* data_load = new LoadFieldInstr( | |
| 681 call->ArgumentAt(0)->value(), | |
| 682 Array::data_offset(), | |
| 683 Type::ZoneHandle(Type::DynamicType())); | |
| 684 data_load->set_result_cid(kArrayCid); | |
| 685 InsertBefore(call, data_load, NULL, Definition::kValue); | |
| 686 | |
| 687 LoadFieldInstr* length_load = new LoadFieldInstr( | |
| 688 new Value(data_load), | |
| 689 Array::length_offset(), | |
| 690 Type::ZoneHandle(Type::SmiType())); | |
| 691 length_load->set_result_cid(kSmiCid); | |
| 692 | |
| 693 call->ReplaceWith(length_load, current_iterator()); | |
| 694 RemovePushArguments(call); | |
| 695 } | |
| 696 | |
| 697 | |
| 698 void FlowGraphOptimizer::InlineStringLengthGetter(InstanceCallInstr* call) { | |
| 699 // Check receiver class. | |
| 700 AddCheckClass(call, call->ArgumentAt(0)->value()->Copy()); | |
| 701 | |
| 702 const bool is_immutable = true; // String length is immutable. | |
| 703 LoadFieldInstr* load = new LoadFieldInstr( | |
| 704 call->ArgumentAt(0)->value(), | |
| 705 String::length_offset(), | |
| 706 Type::ZoneHandle(Type::SmiType()), | |
| 707 is_immutable); | |
| 708 load->set_result_cid(kSmiCid); | |
| 709 call->ReplaceWith(load, current_iterator()); | |
| 710 RemovePushArguments(call); | |
| 711 } | |
| 712 | |
| 713 | |
| 714 void FlowGraphOptimizer::InlineStringIsEmptyTester(InstanceCallInstr* call) { | |
| 715 // Check receiver class. | |
| 716 AddCheckClass(call, call->ArgumentAt(0)->value()->Copy()); | |
| 717 // Get string length. | |
| 718 const bool is_immutable = true; // String length is immutable. | |
| 719 LoadFieldInstr* load = new LoadFieldInstr( | |
| 720 call->ArgumentAt(0)->value(), | |
| 721 String::length_offset(), | |
| 722 Type::ZoneHandle(Type::SmiType()), | |
| 723 is_immutable); | |
| 724 load->set_result_cid(kSmiCid); | |
|
Florian Schneider
2012/09/26 08:36:12
Maybe you can even share the code for building the
srdjan
2012/09/28 16:56:56
Done.
| |
| 725 InsertBefore(call, load, NULL, Definition::kValue); | |
| 726 | |
| 727 ConstantInstr* zero = new ConstantInstr(Smi::Handle(Smi::New(0))); | |
| 728 InsertBefore(call, zero, NULL, Definition::kValue); | |
| 729 | |
| 730 StrictCompareInstr* compare = | |
| 731 new StrictCompareInstr(Token::kEQ_STRICT, | |
| 732 new Value(load), | |
| 733 new Value(zero)); | |
| 734 call->ReplaceWith(compare, current_iterator()); | |
| 735 RemovePushArguments(call); | |
| 736 } | |
| 737 | |
| 738 | |
| 629 // Only unique implicit instance getters can be currently handled. | 739 // Only unique implicit instance getters can be currently handled. |
| 630 bool FlowGraphOptimizer::TryInlineInstanceGetter(InstanceCallInstr* call) { | 740 bool FlowGraphOptimizer::TryInlineInstanceGetter(InstanceCallInstr* call) { |
| 631 ASSERT(call->HasICData()); | 741 ASSERT(call->HasICData()); |
| 632 const ICData& ic_data = *call->ic_data(); | 742 const ICData& ic_data = *call->ic_data(); |
| 633 if (ic_data.NumberOfChecks() == 0) { | 743 if (ic_data.NumberOfChecks() == 0) { |
| 634 // No type feedback collected. | 744 // No type feedback collected. |
| 635 return false; | 745 return false; |
| 636 } | 746 } |
| 637 Function& target = Function::Handle(); | 747 Function& target = Function::Handle(ic_data.GetTargetAt(0)); |
| 638 GrowableArray<intptr_t> class_ids; | |
| 639 ic_data.GetCheckAt(0, &class_ids, &target); | |
| 640 ASSERT(class_ids.length() == 1); | |
| 641 | |
| 642 if (target.kind() == RawFunction::kImplicitGetter) { | 748 if (target.kind() == RawFunction::kImplicitGetter) { |
| 643 if (!ic_data.HasOneTarget()) { | 749 if (!ic_data.HasOneTarget()) { |
| 644 // TODO(srdjan): Implement for mutiple targets. | 750 // TODO(srdjan): Implement for mutiple targets. |
| 645 return false; | 751 return false; |
| 646 } | 752 } |
| 647 // Inline implicit instance getter. | 753 InlineImplicitInstanceGetter(call); |
| 648 const String& field_name = | |
| 649 String::Handle(Field::NameFromGetter(call->function_name())); | |
| 650 const Field& field = Field::Handle(GetField(class_ids[0], field_name)); | |
| 651 ASSERT(!field.IsNull()); | |
| 652 | |
| 653 if (InstanceCallNeedsClassCheck(call)) { | |
| 654 AddCheckClass(call, call->ArgumentAt(0)->value()->Copy()); | |
| 655 } | |
| 656 // Detach environment from the original instruction because it can't | |
| 657 // deoptimize. | |
| 658 call->set_env(NULL); | |
| 659 LoadFieldInstr* load = new LoadFieldInstr( | |
| 660 call->ArgumentAt(0)->value(), | |
| 661 field.Offset(), | |
| 662 AbstractType::ZoneHandle(field.type())); | |
| 663 call->ReplaceWith(load, current_iterator()); | |
| 664 RemovePushArguments(call); | |
| 665 return true; | 754 return true; |
| 666 } | 755 } |
| 667 | 756 |
| 668 // Not an implicit getter. | 757 // Not an implicit getter. |
| 669 MethodRecognizer::Kind recognized_kind = | 758 MethodRecognizer::Kind recognized_kind = |
| 670 MethodRecognizer::RecognizeKind(target); | 759 MethodRecognizer::RecognizeKind(target); |
| 671 | 760 |
| 672 // VM objects length getter. | 761 // VM objects length getter. |
| 673 if ((recognized_kind == MethodRecognizer::kObjectArrayLength) || | 762 if ((recognized_kind == MethodRecognizer::kObjectArrayLength) || |
| 674 (recognized_kind == MethodRecognizer::kImmutableArrayLength) || | 763 (recognized_kind == MethodRecognizer::kImmutableArrayLength) || |
| 675 (recognized_kind == MethodRecognizer::kGrowableArrayLength)) { | 764 (recognized_kind == MethodRecognizer::kGrowableArrayLength)) { |
| 676 if (!ic_data.HasOneTarget()) { | 765 if (!ic_data.HasOneTarget()) { |
| 677 // TODO(srdjan): Implement for mutiple targets. | 766 // TODO(srdjan): Implement for mutiple targets. |
| 678 return false; | 767 return false; |
| 679 } | 768 } |
| 680 intptr_t length_offset = -1; | |
| 681 bool is_immutable = false; | |
| 682 switch (recognized_kind) { | 769 switch (recognized_kind) { |
| 683 case MethodRecognizer::kObjectArrayLength: | 770 case MethodRecognizer::kObjectArrayLength: |
| 684 case MethodRecognizer::kImmutableArrayLength: | 771 case MethodRecognizer::kImmutableArrayLength: |
| 685 length_offset = Array::length_offset(); | 772 InlineArrayLengthGetter(call, Array::length_offset(), true); |
| 686 is_immutable = true; | |
| 687 break; | 773 break; |
| 688 case MethodRecognizer::kGrowableArrayLength: | 774 case MethodRecognizer::kGrowableArrayLength: |
| 689 length_offset = GrowableObjectArray::length_offset(); | 775 InlineArrayLengthGetter(call, |
| 776 GrowableObjectArray::length_offset(), | |
| 777 false); | |
| 690 break; | 778 break; |
| 691 default: | 779 default: |
| 692 UNREACHABLE(); | 780 UNREACHABLE(); |
| 693 } | 781 } |
| 694 // Check receiver class. | |
| 695 AddCheckClass(call, call->ArgumentAt(0)->value()->Copy()); | |
| 696 | |
| 697 LoadFieldInstr* load = new LoadFieldInstr( | |
| 698 call->ArgumentAt(0)->value(), | |
| 699 length_offset, | |
| 700 Type::ZoneHandle(Type::SmiType()), | |
| 701 is_immutable); | |
| 702 load->set_result_cid(kSmiCid); | |
| 703 call->ReplaceWith(load, current_iterator()); | |
| 704 RemovePushArguments(call); | |
| 705 return true; | 782 return true; |
| 706 } | 783 } |
| 707 | 784 |
| 708 if (recognized_kind == MethodRecognizer::kGrowableArrayCapacity) { | 785 if (recognized_kind == MethodRecognizer::kGrowableArrayCapacity) { |
| 709 // Check receiver class. | 786 InlineGArrayCapacityGetter(call); |
| 710 AddCheckClass(call, call->ArgumentAt(0)->value()->Copy()); | |
| 711 | |
| 712 // TODO(srdjan): type of load should be GrowableObjectArrayType. | |
| 713 LoadFieldInstr* data_load = new LoadFieldInstr( | |
| 714 call->ArgumentAt(0)->value(), | |
| 715 Array::data_offset(), | |
| 716 Type::ZoneHandle(Type::DynamicType())); | |
| 717 data_load->set_result_cid(kArrayCid); | |
| 718 InsertBefore(call, data_load, NULL, Definition::kValue); | |
| 719 | |
| 720 LoadFieldInstr* length_load = new LoadFieldInstr( | |
| 721 new Value(data_load), | |
| 722 Array::length_offset(), | |
| 723 Type::ZoneHandle(Type::SmiType())); | |
| 724 length_load->set_result_cid(kSmiCid); | |
| 725 | |
| 726 call->ReplaceWith(length_load, current_iterator()); | |
| 727 RemovePushArguments(call); | |
| 728 return true; | 787 return true; |
| 729 } | 788 } |
| 730 | 789 |
| 731 if (recognized_kind == MethodRecognizer::kStringBaseLength) { | 790 if (recognized_kind == MethodRecognizer::kStringBaseLength) { |
| 732 if (!ic_data.HasOneTarget()) { | 791 if (!ic_data.HasOneTarget()) { |
| 733 // Target is not only StringBase_get_length. | 792 // Target is not only StringBase_get_length. |
| 734 return false; | 793 return false; |
| 735 } | 794 } |
| 736 // Check receiver class. | 795 InlineStringLengthGetter(call); |
| 737 AddCheckClass(call, call->ArgumentAt(0)->value()->Copy()); | |
| 738 | |
| 739 const bool is_immutable = true; // String length is immutable. | |
| 740 LoadFieldInstr* load = new LoadFieldInstr( | |
| 741 call->ArgumentAt(0)->value(), | |
| 742 String::length_offset(), | |
| 743 Type::ZoneHandle(Type::SmiType()), | |
| 744 is_immutable); | |
| 745 load->set_result_cid(kSmiCid); | |
| 746 call->ReplaceWith(load, current_iterator()); | |
| 747 RemovePushArguments(call); | |
| 748 return true; | 796 return true; |
| 749 } | 797 } |
| 798 | |
| 750 return false; | 799 return false; |
| 751 } | 800 } |
| 752 | 801 |
| 753 | 802 |
| 754 // Inline only simple, frequently called core library methods. | 803 // Inline only simple, frequently called core library methods. |
| 755 bool FlowGraphOptimizer::TryInlineInstanceMethod(InstanceCallInstr* call) { | 804 bool FlowGraphOptimizer::TryInlineInstanceMethod(InstanceCallInstr* call) { |
| 756 ASSERT(call->HasICData()); | 805 ASSERT(call->HasICData()); |
| 757 const ICData& ic_data = *call->ic_data(); | 806 const ICData& ic_data = *call->ic_data(); |
| 758 if ((ic_data.NumberOfChecks() == 0) || !ic_data.HasOneTarget()) { | 807 if ((ic_data.NumberOfChecks() == 0) || !ic_data.HasOneTarget()) { |
| 759 // No type feedback collected. | 808 // No type feedback collected. |
| 760 return false; | 809 return false; |
| 761 } | 810 } |
| 762 Function& target = Function::Handle(); | 811 Function& target = Function::Handle(); |
| 763 GrowableArray<intptr_t> class_ids; | 812 GrowableArray<intptr_t> class_ids; |
| 764 ic_data.GetCheckAt(0, &class_ids, &target); | 813 ic_data.GetCheckAt(0, &class_ids, &target); |
| 765 MethodRecognizer::Kind recognized_kind = | 814 MethodRecognizer::Kind recognized_kind = |
| 766 MethodRecognizer::RecognizeKind(target); | 815 MethodRecognizer::RecognizeKind(target); |
| 767 | 816 |
| 768 if ((recognized_kind == MethodRecognizer::kDoubleToDouble) && | 817 if ((recognized_kind == MethodRecognizer::kDoubleToDouble) && |
| 769 (class_ids[0] == kDoubleCid)) { | 818 (class_ids[0] == kDoubleCid)) { |
| 770 DoubleToDoubleInstr* d2d_instr = | 819 DoubleToDoubleInstr* d2d_instr = |
| 771 new DoubleToDoubleInstr(call->ArgumentAt(0)->value(), call); | 820 new DoubleToDoubleInstr(call->ArgumentAt(0)->value(), call); |
| 772 call->ReplaceWith(d2d_instr, current_iterator()); | 821 call->ReplaceWith(d2d_instr, current_iterator()); |
| 773 RemovePushArguments(call); | 822 RemovePushArguments(call); |
| 774 return true; | 823 return true; |
| 775 } | 824 } |
| 825 | |
| 776 if ((recognized_kind == MethodRecognizer::kIntegerToDouble) && | 826 if ((recognized_kind == MethodRecognizer::kIntegerToDouble) && |
| 777 (class_ids[0] == kSmiCid)) { | 827 (class_ids[0] == kSmiCid)) { |
| 778 SmiToDoubleInstr* s2d_instr = new SmiToDoubleInstr(call); | 828 SmiToDoubleInstr* s2d_instr = new SmiToDoubleInstr(call); |
| 779 call->ReplaceWith(s2d_instr, current_iterator()); | 829 call->ReplaceWith(s2d_instr, current_iterator()); |
| 780 // Pushed arguments are not removed because SmiToDouble is implemented | 830 // Pushed arguments are not removed because SmiToDouble is implemented |
| 781 // as a call. | 831 // as a call. |
| 782 return true; | 832 return true; |
| 783 } | 833 } |
| 834 | |
| 835 if (recognized_kind == MethodRecognizer::kStringBaseIsEmpty) { | |
| 836 if (!ic_data.HasOneTarget()) { | |
| 837 // Target is not only StringBase_get_length. | |
| 838 return false; | |
| 839 } | |
| 840 InlineStringIsEmptyTester(call); | |
| 841 return true; | |
| 842 } | |
| 843 | |
| 784 return false; | 844 return false; |
| 785 } | 845 } |
| 786 | 846 |
| 787 | 847 |
| 788 void FlowGraphOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { | 848 void FlowGraphOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { |
| 789 if (instr->HasICData() && (instr->ic_data()->NumberOfChecks() > 0)) { | 849 if (instr->HasICData() && (instr->ic_data()->NumberOfChecks() > 0)) { |
| 790 const Token::Kind op_kind = instr->token_kind(); | 850 const Token::Kind op_kind = instr->token_kind(); |
| 791 if (Token::IsIndexOperator(op_kind) && | 851 if (Token::IsIndexOperator(op_kind) && |
| 792 TryReplaceWithArrayOp(instr, op_kind)) { | 852 TryReplaceWithArrayOp(instr, op_kind)) { |
| 793 return; | 853 return; |
| (...skipping 2237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3031 | 3091 |
| 3032 if (FLAG_trace_constant_propagation) { | 3092 if (FLAG_trace_constant_propagation) { |
| 3033 OS::Print("\n==== After constant propagation ====\n"); | 3093 OS::Print("\n==== After constant propagation ====\n"); |
| 3034 FlowGraphPrinter printer(*graph_); | 3094 FlowGraphPrinter printer(*graph_); |
| 3035 printer.PrintBlocks(); | 3095 printer.PrintBlocks(); |
| 3036 } | 3096 } |
| 3037 } | 3097 } |
| 3038 | 3098 |
| 3039 | 3099 |
| 3040 } // namespace dart | 3100 } // namespace dart |
| OLD | NEW |