OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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/aot_optimizer.h" | 5 #include "vm/aot_optimizer.h" |
6 | 6 |
7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
8 #include "vm/branch_optimizer.h" | 8 #include "vm/branch_optimizer.h" |
9 #include "vm/cha.h" | 9 #include "vm/cha.h" |
10 #include "vm/compiler.h" | 10 #include "vm/compiler.h" |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
81 call->checked_argument_count(), false)); | 81 call->checked_argument_count(), false)); |
82 call->set_ic_data(&ic_data); | 82 call->set_ic_data(&ic_data); |
83 } | 83 } |
84 } | 84 } |
85 } | 85 } |
86 current_iterator_ = NULL; | 86 current_iterator_ = NULL; |
87 } | 87 } |
88 } | 88 } |
89 | 89 |
90 | 90 |
| 91 bool AotOptimizer::RecognizeRuntimeTypeGetter(InstanceCallInstr* call) { |
| 92 if ((precompiler_ == NULL) || !precompiler_->get_runtime_type_is_unique()) { |
| 93 return false; |
| 94 } |
| 95 |
| 96 if (call->function_name().raw() != Symbols::GetRuntimeType().raw()) { |
| 97 return false; |
| 98 } |
| 99 |
| 100 // There is only a single function Object.get:runtimeType that can be invoked |
| 101 // by this call. Convert dynamic invocation to a static one. |
| 102 const Class& cls = Class::Handle(Z, I->object_store()->object_class()); |
| 103 const Array& args_desc_array = Array::Handle(Z, |
| 104 ArgumentsDescriptor::New(call->ArgumentCount(), |
| 105 call->argument_names())); |
| 106 ArgumentsDescriptor args_desc(args_desc_array); |
| 107 const Function& function = Function::Handle(Z, |
| 108 Resolver::ResolveDynamicForReceiverClass( |
| 109 cls, |
| 110 call->function_name(), |
| 111 args_desc)); |
| 112 ASSERT(!function.IsNull()); |
| 113 |
| 114 ZoneGrowableArray<PushArgumentInstr*>* args = |
| 115 new (Z) ZoneGrowableArray<PushArgumentInstr*>( |
| 116 call->ArgumentCount()); |
| 117 for (intptr_t i = 0; i < call->ArgumentCount(); i++) { |
| 118 args->Add(call->PushArgumentAt(i)); |
| 119 } |
| 120 StaticCallInstr* static_call = new (Z) StaticCallInstr( |
| 121 call->token_pos(), |
| 122 Function::ZoneHandle(Z, function.raw()), |
| 123 call->argument_names(), |
| 124 args, |
| 125 call->deopt_id()); |
| 126 static_call->set_result_cid(kTypeCid); |
| 127 call->ReplaceWith(static_call, current_iterator()); |
| 128 return true; |
| 129 } |
| 130 |
| 131 |
91 // Optimize instance calls using cid. This is called after optimizer | 132 // Optimize instance calls using cid. This is called after optimizer |
92 // converted instance calls to instructions. Any remaining | 133 // converted instance calls to instructions. Any remaining |
93 // instance calls are either megamorphic calls, cannot be optimized or | 134 // instance calls are either megamorphic calls, cannot be optimized or |
94 // have no runtime type feedback collected. | 135 // have no runtime type feedback collected. |
95 // Attempts to convert an instance call (IC call) using propagated class-ids, | 136 // Attempts to convert an instance call (IC call) using propagated class-ids, |
96 // e.g., receiver class id, guarded-cid, or by guessing cid-s. | 137 // e.g., receiver class id, guarded-cid, or by guessing cid-s. |
97 void AotOptimizer::ApplyClassIds() { | 138 void AotOptimizer::ApplyClassIds() { |
98 ASSERT(current_iterator_ == NULL); | 139 ASSERT(current_iterator_ == NULL); |
99 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); | 140 for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); |
100 !block_it.Done(); | 141 !block_it.Done(); |
(...skipping 498 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
599 to_remove_right->RemoveFromGraph(); | 640 to_remove_right->RemoveFromGraph(); |
600 } | 641 } |
601 return true; | 642 return true; |
602 } | 643 } |
603 return false; | 644 return false; |
604 } | 645 } |
605 | 646 |
606 | 647 |
607 static bool SmiFitsInDouble() { return kSmiBits < 53; } | 648 static bool SmiFitsInDouble() { return kSmiBits < 53; } |
608 | 649 |
| 650 |
| 651 static bool IsGetRuntimeType(Definition* defn) { |
| 652 StaticCallInstr* call = defn->AsStaticCall(); |
| 653 return (call != NULL) && |
| 654 (call->function().recognized_kind() == |
| 655 MethodRecognizer::kObjectRuntimeType); |
| 656 } |
| 657 |
| 658 |
| 659 // Recognize a.runtimeType == b.runtimeType and fold it into |
| 660 // Object._haveSameRuntimeType(a, b). |
| 661 // Note: this optimization is not speculative. |
| 662 bool AotOptimizer::TryReplaceWithHaveSameRuntimeType(InstanceCallInstr* call) { |
| 663 const ICData& ic_data = *call->ic_data(); |
| 664 ASSERT(ic_data.NumArgsTested() == 2); |
| 665 |
| 666 ASSERT(call->ArgumentCount() == 2); |
| 667 Definition* left = call->ArgumentAt(0); |
| 668 Definition* right = call->ArgumentAt(1); |
| 669 |
| 670 if (IsGetRuntimeType(left) && left->input_use_list()->IsSingleUse() && |
| 671 IsGetRuntimeType(right) && right->input_use_list()->IsSingleUse()) { |
| 672 const Class& cls = Class::Handle(Z, I->object_store()->object_class()); |
| 673 const Function& have_same_runtime_type = Function::ZoneHandle(Z, |
| 674 cls.LookupStaticFunctionAllowPrivate(Symbols::HaveSameRuntimeType())); |
| 675 ASSERT(!have_same_runtime_type.IsNull()); |
| 676 |
| 677 ZoneGrowableArray<PushArgumentInstr*>* args = |
| 678 new (Z) ZoneGrowableArray<PushArgumentInstr*>(2); |
| 679 PushArgumentInstr* arg = new (Z) PushArgumentInstr( |
| 680 new (Z) Value(left->ArgumentAt(0))); |
| 681 InsertBefore(call, arg, NULL, FlowGraph::kEffect); |
| 682 args->Add(arg); |
| 683 arg = new (Z) PushArgumentInstr( |
| 684 new (Z) Value(right->ArgumentAt(0))); |
| 685 InsertBefore(call, arg, NULL, FlowGraph::kEffect); |
| 686 args->Add(arg); |
| 687 StaticCallInstr* static_call = new (Z) StaticCallInstr( |
| 688 call->token_pos(), |
| 689 have_same_runtime_type, |
| 690 Object::null_array(), // argument_names |
| 691 args, |
| 692 call->deopt_id()); |
| 693 static_call->set_result_cid(kBoolCid); |
| 694 ReplaceCall(call, static_call); |
| 695 return true; |
| 696 } |
| 697 |
| 698 return false; |
| 699 } |
| 700 |
| 701 |
609 bool AotOptimizer::TryReplaceWithEqualityOp(InstanceCallInstr* call, | 702 bool AotOptimizer::TryReplaceWithEqualityOp(InstanceCallInstr* call, |
610 Token::Kind op_kind) { | 703 Token::Kind op_kind) { |
611 const ICData& ic_data = *call->ic_data(); | 704 const ICData& ic_data = *call->ic_data(); |
612 ASSERT(ic_data.NumArgsTested() == 2); | 705 ASSERT(ic_data.NumArgsTested() == 2); |
613 | 706 |
614 ASSERT(call->ArgumentCount() == 2); | 707 ASSERT(call->ArgumentCount() == 2); |
615 Definition* left = call->ArgumentAt(0); | 708 Definition* left = call->ArgumentAt(0); |
616 Definition* right = call->ArgumentAt(1); | 709 Definition* right = call->ArgumentAt(1); |
617 | 710 |
618 intptr_t cid = kIllegalCid; | 711 intptr_t cid = kIllegalCid; |
619 if (HasOnlyTwoOf(ic_data, kOneByteStringCid)) { | 712 if (HasOnlyTwoOf(ic_data, kOneByteStringCid)) { |
620 if (TryStringLengthOneEquality(call, op_kind)) { | 713 return TryStringLengthOneEquality(call, op_kind); |
621 return true; | |
622 } else { | |
623 return false; | |
624 } | |
625 } else if (HasOnlyTwoOf(ic_data, kSmiCid)) { | 714 } else if (HasOnlyTwoOf(ic_data, kSmiCid)) { |
626 InsertBefore(call, | 715 InsertBefore(call, |
627 new(Z) CheckSmiInstr(new(Z) Value(left), | 716 new(Z) CheckSmiInstr(new(Z) Value(left), |
628 call->deopt_id(), | 717 call->deopt_id(), |
629 call->token_pos()), | 718 call->token_pos()), |
630 call->env(), | 719 call->env(), |
631 FlowGraph::kEffect); | 720 FlowGraph::kEffect); |
632 InsertBefore(call, | 721 InsertBefore(call, |
633 new(Z) CheckSmiInstr(new(Z) Value(right), | 722 new(Z) CheckSmiInstr(new(Z) Value(right), |
634 call->deopt_id(), | 723 call->deopt_id(), |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
680 const ICData& unary_checks_1 = | 769 const ICData& unary_checks_1 = |
681 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecksForArgNr(1)); | 770 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecksForArgNr(1)); |
682 AddCheckClass(right, | 771 AddCheckClass(right, |
683 unary_checks_1, | 772 unary_checks_1, |
684 call->deopt_id(), | 773 call->deopt_id(), |
685 call->env(), | 774 call->env(), |
686 call); | 775 call); |
687 cid = kSmiCid; | 776 cid = kSmiCid; |
688 } else { | 777 } else { |
689 // Shortcut for equality with null. | 778 // Shortcut for equality with null. |
| 779 // TODO(vegorov): this optimization is not speculative and should |
| 780 // be hoisted out of this function. |
690 ConstantInstr* right_const = right->AsConstant(); | 781 ConstantInstr* right_const = right->AsConstant(); |
691 ConstantInstr* left_const = left->AsConstant(); | 782 ConstantInstr* left_const = left->AsConstant(); |
692 if ((right_const != NULL && right_const->value().IsNull()) || | 783 if ((right_const != NULL && right_const->value().IsNull()) || |
693 (left_const != NULL && left_const->value().IsNull())) { | 784 (left_const != NULL && left_const->value().IsNull())) { |
694 StrictCompareInstr* comp = | 785 StrictCompareInstr* comp = |
695 new(Z) StrictCompareInstr(call->token_pos(), | 786 new(Z) StrictCompareInstr(call->token_pos(), |
696 Token::kEQ_STRICT, | 787 Token::kEQ_STRICT, |
697 new(Z) Value(left), | 788 new(Z) Value(left), |
698 new(Z) Value(right), | 789 new(Z) Value(right), |
699 false); // No number check. | 790 false); // No number check. |
(...skipping 1027 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1727 } | 1818 } |
1728 if (Token::IsTypeCastOperator(op_kind)) { | 1819 if (Token::IsTypeCastOperator(op_kind)) { |
1729 ReplaceWithTypeCast(instr); | 1820 ReplaceWithTypeCast(instr); |
1730 return; | 1821 return; |
1731 } | 1822 } |
1732 | 1823 |
1733 if (TryInlineFieldAccess(instr)) { | 1824 if (TryInlineFieldAccess(instr)) { |
1734 return; | 1825 return; |
1735 } | 1826 } |
1736 | 1827 |
| 1828 if (RecognizeRuntimeTypeGetter(instr)) { |
| 1829 return; |
| 1830 } |
| 1831 |
| 1832 if ((op_kind == Token::kEQ) && TryReplaceWithHaveSameRuntimeType(instr)) { |
| 1833 return; |
| 1834 } |
| 1835 |
1737 const ICData& unary_checks = | 1836 const ICData& unary_checks = |
1738 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks()); | 1837 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks()); |
1739 if (IsAllowedForInlining(instr->deopt_id()) && | 1838 if (IsAllowedForInlining(instr->deopt_id()) && |
1740 (unary_checks.NumberOfChecks() > 0)) { | 1839 (unary_checks.NumberOfChecks() > 0)) { |
1741 if ((op_kind == Token::kINDEX) && TryReplaceWithIndexedOp(instr)) { | 1840 if ((op_kind == Token::kINDEX) && TryReplaceWithIndexedOp(instr)) { |
1742 return; | 1841 return; |
1743 } | 1842 } |
1744 if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithIndexedOp(instr)) { | 1843 if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithIndexedOp(instr)) { |
1745 return; | 1844 return; |
1746 } | 1845 } |
(...skipping 431 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2178 check->env(), FlowGraph::kEffect); | 2277 check->env(), FlowGraph::kEffect); |
2179 current_iterator()->RemoveCurrentFromGraph(); | 2278 current_iterator()->RemoveCurrentFromGraph(); |
2180 } | 2279 } |
2181 } | 2280 } |
2182 } | 2281 } |
2183 } | 2282 } |
2184 | 2283 |
2185 #endif // DART_PRECOMPILER | 2284 #endif // DART_PRECOMPILER |
2186 | 2285 |
2187 } // namespace dart | 2286 } // namespace dart |
OLD | NEW |