| 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 #if !defined(DART_PRECOMPILED_RUNTIME) | 4 #if !defined(DART_PRECOMPILED_RUNTIME) |
| 5 #include "vm/flow_graph_inliner.h" | 5 #include "vm/flow_graph_inliner.h" |
| 6 | 6 |
| 7 #include "vm/aot_optimizer.h" | 7 #include "vm/aot_optimizer.h" |
| 8 #include "vm/precompiler.h" | 8 #include "vm/precompiler.h" |
| 9 #include "vm/block_scheduler.h" | 9 #include "vm/block_scheduler.h" |
| 10 #include "vm/branch_optimizer.h" | 10 #include "vm/branch_optimizer.h" |
| (...skipping 1654 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1665 | 1665 |
| 1666 Definition* receiver = call_->ArgumentAt(0); | 1666 Definition* receiver = call_->ArgumentAt(0); |
| 1667 // There are at least two variants including non-inlined ones, so we have | 1667 // There are at least two variants including non-inlined ones, so we have |
| 1668 // at least one branch on the class id. | 1668 // at least one branch on the class id. |
| 1669 LoadClassIdInstr* load_cid = | 1669 LoadClassIdInstr* load_cid = |
| 1670 new (Z) LoadClassIdInstr(new (Z) Value(receiver)); | 1670 new (Z) LoadClassIdInstr(new (Z) Value(receiver)); |
| 1671 load_cid->set_ssa_temp_index(owner_->caller_graph()->alloc_ssa_temp_index()); | 1671 load_cid->set_ssa_temp_index(owner_->caller_graph()->alloc_ssa_temp_index()); |
| 1672 cursor = AppendInstruction(cursor, load_cid); | 1672 cursor = AppendInstruction(cursor, load_cid); |
| 1673 for (intptr_t i = 0; i < inlined_variants_.length(); ++i) { | 1673 for (intptr_t i = 0; i < inlined_variants_.length(); ++i) { |
| 1674 const CidRange& variant = inlined_variants_[i]; | 1674 const CidRange& variant = inlined_variants_[i]; |
| 1675 bool test_is_range = !variant.IsSingleCid(); | |
| 1676 bool is_last_test = (i == inlined_variants_.length() - 1); | 1675 bool is_last_test = (i == inlined_variants_.length() - 1); |
| 1677 // 1. Guard the body with a class id check. We don't need any check if | 1676 // 1. Guard the body with a class id check. We don't need any check if |
| 1678 // it's the last test and global analysis has told us that the call is | 1677 // it's the last test and global analysis has told us that the call is |
| 1679 // complete. | 1678 // complete. |
| 1680 if (is_last_test && non_inlined_variants_->is_empty()) { | 1679 if (is_last_test && non_inlined_variants_->is_empty()) { |
| 1681 // If it is the last variant use a check class id instruction which can | 1680 // If it is the last variant use a check class id instruction which can |
| 1682 // deoptimize, followed unconditionally by the body. Omit the check if | 1681 // deoptimize, followed unconditionally by the body. Omit the check if |
| 1683 // we know that we have covered all possible classes. | 1682 // we know that we have covered all possible classes. |
| 1684 if (!call_->complete()) { | 1683 if (!call_->complete()) { |
| 1685 RedefinitionInstr* cid_redefinition = | 1684 RedefinitionInstr* cid_redefinition = |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1725 current_block->set_last_instruction(goto_join); | 1724 current_block->set_last_instruction(goto_join); |
| 1726 } else { | 1725 } else { |
| 1727 // There is no possibility of a TargetEntry (the first entry to a | 1726 // There is no possibility of a TargetEntry (the first entry to a |
| 1728 // shared inlined body) because this is the last inlined entry. | 1727 // shared inlined body) because this is the last inlined entry. |
| 1729 UNREACHABLE(); | 1728 UNREACHABLE(); |
| 1730 } | 1729 } |
| 1731 cursor = NULL; | 1730 cursor = NULL; |
| 1732 } else { | 1731 } else { |
| 1733 // For all variants except the last, use a branch on the loaded class | 1732 // For all variants except the last, use a branch on the loaded class |
| 1734 // id. | 1733 // id. |
| 1735 const Smi& cid = Smi::ZoneHandle(Smi::New(variant.cid_start)); | 1734 ComparisonInstr* compare; |
| 1736 ConstantInstr* cid_constant = owner_->caller_graph()->GetConstant(cid); | |
| 1737 BranchInstr* branch; | |
| 1738 BranchInstr* upper_limit_branch = NULL; | |
| 1739 BlockEntryInstr* cid_test_entry_block = current_block; | 1735 BlockEntryInstr* cid_test_entry_block = current_block; |
| 1740 if (test_is_range) { | 1736 if (variant.IsSingleCid()) { |
| 1741 // Double branch for testing a range of Cids. TODO(erikcorry): Make a | 1737 const Smi& cid = Smi::ZoneHandle(Smi::New(variant.cid_start)); |
| 1742 // special instruction that uses subtraction and unsigned comparison to | 1738 ConstantInstr* cid_constant = owner_->caller_graph()->GetConstant(cid); |
| 1743 // do this with a single branch. | 1739 compare = new StrictCompareInstr( |
| 1744 const Smi& cid_end = Smi::ZoneHandle(Smi::New(variant.cid_end)); | |
| 1745 ConstantInstr* cid_constant_end = | |
| 1746 owner_->caller_graph()->GetConstant(cid_end); | |
| 1747 RelationalOpInstr* compare_top = new RelationalOpInstr( | |
| 1748 call_->instance_call()->token_pos(), Token::kLTE, | |
| 1749 new Value(load_cid), new Value(cid_constant_end), kSmiCid, | |
| 1750 call_->deopt_id()); | |
| 1751 BranchInstr* branch_top = upper_limit_branch = | |
| 1752 new BranchInstr(compare_top, Thread::kNoDeoptId); | |
| 1753 branch_top->InheritDeoptTarget(zone(), call_); | |
| 1754 cursor = AppendInstruction(cursor, branch_top); | |
| 1755 current_block->set_last_instruction(branch_top); | |
| 1756 | |
| 1757 TargetEntryInstr* below_target = new TargetEntryInstr( | |
| 1758 AllocateBlockId(), try_idx, Thread::kNoDeoptId); | |
| 1759 below_target->InheritDeoptTarget(zone(), call_); | |
| 1760 current_block->AddDominatedBlock(below_target); | |
| 1761 cursor = current_block = below_target; | |
| 1762 *branch_top->true_successor_address() = below_target; | |
| 1763 | |
| 1764 RelationalOpInstr* compare_bottom = new RelationalOpInstr( | |
| 1765 call_->instance_call()->token_pos(), Token::kGTE, | |
| 1766 new Value(load_cid), new Value(cid_constant), kSmiCid, | |
| 1767 call_->deopt_id()); | |
| 1768 branch = new BranchInstr(compare_bottom, Thread::kNoDeoptId); | |
| 1769 } else { | |
| 1770 StrictCompareInstr* compare = new StrictCompareInstr( | |
| 1771 call_->instance_call()->token_pos(), Token::kEQ_STRICT, | 1740 call_->instance_call()->token_pos(), Token::kEQ_STRICT, |
| 1772 new Value(load_cid), new Value(cid_constant), | 1741 new Value(load_cid), new Value(cid_constant), |
| 1773 /* number_check = */ false, Thread::kNoDeoptId); | 1742 /* number_check = */ false, Thread::kNoDeoptId); |
| 1774 branch = new BranchInstr(compare, Thread::kNoDeoptId); | 1743 } else { |
| 1744 compare = new SmiRangeComparisonInstr( |
| 1745 call_->instance_call()->token_pos(), new Value(load_cid), |
| 1746 variant.cid_start, variant.cid_end); |
| 1775 } | 1747 } |
| 1748 BranchInstr* branch = new BranchInstr(compare, Thread::kNoDeoptId); |
| 1776 | 1749 |
| 1777 branch->InheritDeoptTarget(zone(), call_); | 1750 branch->InheritDeoptTarget(zone(), call_); |
| 1778 cursor = AppendInstruction(cursor, branch); | 1751 cursor = AppendInstruction(cursor, branch); |
| 1779 current_block->set_last_instruction(branch); | 1752 current_block->set_last_instruction(branch); |
| 1780 cursor = NULL; | 1753 cursor = NULL; |
| 1781 | 1754 |
| 1782 // 2. Handle a match by linking to the inlined body. There are three | 1755 // 2. Handle a match by linking to the inlined body. There are three |
| 1783 // cases (unshared, shared first predecessor, and shared subsequent | 1756 // cases (unshared, shared first predecessor, and shared subsequent |
| 1784 // predecessors). | 1757 // predecessors). |
| 1785 BlockEntryInstr* callee_entry = inlined_entries_[i]; | 1758 BlockEntryInstr* callee_entry = inlined_entries_[i]; |
| (...skipping 30 matching lines...) Expand all Loading... |
| 1816 // 3. Prepare to handle a match failure on the next iteration or the | 1789 // 3. Prepare to handle a match failure on the next iteration or the |
| 1817 // fall-through code below for non-inlined variants. | 1790 // fall-through code below for non-inlined variants. |
| 1818 | 1791 |
| 1819 TargetEntryInstr* false_target = | 1792 TargetEntryInstr* false_target = |
| 1820 new TargetEntryInstr(AllocateBlockId(), try_idx, Thread::kNoDeoptId); | 1793 new TargetEntryInstr(AllocateBlockId(), try_idx, Thread::kNoDeoptId); |
| 1821 false_target->InheritDeoptTarget(zone(), call_); | 1794 false_target->InheritDeoptTarget(zone(), call_); |
| 1822 *branch->false_successor_address() = false_target; | 1795 *branch->false_successor_address() = false_target; |
| 1823 cid_test_entry_block->AddDominatedBlock(false_target); | 1796 cid_test_entry_block->AddDominatedBlock(false_target); |
| 1824 | 1797 |
| 1825 cursor = current_block = false_target; | 1798 cursor = current_block = false_target; |
| 1826 | |
| 1827 if (test_is_range) { | |
| 1828 // If we tested against a range of Cids there are two different tests | |
| 1829 // that can go to the no-cid-match target. | |
| 1830 JoinEntryInstr* join = | |
| 1831 new JoinEntryInstr(AllocateBlockId(), try_idx, Thread::kNoDeoptId); | |
| 1832 TargetEntryInstr* false_target2 = new TargetEntryInstr( | |
| 1833 AllocateBlockId(), try_idx, Thread::kNoDeoptId); | |
| 1834 *upper_limit_branch->false_successor_address() = false_target2; | |
| 1835 cid_test_entry_block->AddDominatedBlock(false_target2); | |
| 1836 cid_test_entry_block->AddDominatedBlock(join); | |
| 1837 GotoInstr* goto_1 = new GotoInstr(join, Thread::kNoDeoptId); | |
| 1838 GotoInstr* goto_2 = new GotoInstr(join, Thread::kNoDeoptId); | |
| 1839 false_target->LinkTo(goto_1); | |
| 1840 false_target2->LinkTo(goto_2); | |
| 1841 false_target->set_last_instruction(goto_1); | |
| 1842 false_target2->set_last_instruction(goto_2); | |
| 1843 | |
| 1844 join->InheritDeoptTarget(zone(), call_); | |
| 1845 false_target2->InheritDeoptTarget(zone(), call_); | |
| 1846 goto_1->InheritDeoptTarget(zone(), call_); | |
| 1847 goto_2->InheritDeoptTarget(zone(), call_); | |
| 1848 | |
| 1849 cursor = current_block = join; | |
| 1850 } | |
| 1851 } | 1799 } |
| 1852 } | 1800 } |
| 1853 | 1801 |
| 1854 // Handle any non-inlined variants. | 1802 // Handle any non-inlined variants. |
| 1855 if (!non_inlined_variants_->is_empty()) { | 1803 if (!non_inlined_variants_->is_empty()) { |
| 1856 // Move push arguments of the call. | 1804 // Move push arguments of the call. |
| 1857 for (intptr_t i = 0; i < call_->ArgumentCount(); ++i) { | 1805 for (intptr_t i = 0; i < call_->ArgumentCount(); ++i) { |
| 1858 PushArgumentInstr* push = call_->PushArgumentAt(i); | 1806 PushArgumentInstr* push = call_->PushArgumentAt(i); |
| 1859 push->ReplaceUsesWith(push->value()->definition()); | 1807 push->ReplaceUsesWith(push->value()->definition()); |
| 1860 push->previous()->LinkTo(push->next()); | 1808 push->previous()->LinkTo(push->next()); |
| (...skipping 1906 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3767 } | 3715 } |
| 3768 | 3716 |
| 3769 default: | 3717 default: |
| 3770 return false; | 3718 return false; |
| 3771 } | 3719 } |
| 3772 } | 3720 } |
| 3773 | 3721 |
| 3774 | 3722 |
| 3775 } // namespace dart | 3723 } // namespace dart |
| 3776 #endif // !defined(DART_PRECOMPILED_RUNTIME) | 3724 #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| OLD | NEW |