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_allocator.h" | 5 #include "vm/flow_graph_allocator.h" |
| 6 | 6 |
| 7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
| 8 #include "vm/intermediate_language.h" | 8 #include "vm/intermediate_language.h" |
| 9 #include "vm/il_printer.h" | 9 #include "vm/il_printer.h" |
| 10 #include "vm/flow_graph_builder.h" | 10 #include "vm/flow_graph_builder.h" |
| (...skipping 399 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 410 | 410 |
| 411 for (intptr_t i = 0; i < live_ranges_.length(); i++) { | 411 for (intptr_t i = 0; i < live_ranges_.length(); i++) { |
| 412 if (live_ranges_[i] != NULL) { | 412 if (live_ranges_[i] != NULL) { |
| 413 live_ranges_[i]->Print(); | 413 live_ranges_[i]->Print(); |
| 414 } | 414 } |
| 415 } | 415 } |
| 416 } | 416 } |
| 417 | 417 |
| 418 | 418 |
| 419 void FlowGraphAllocator::BuildLiveRanges() { | 419 void FlowGraphAllocator::BuildLiveRanges() { |
| 420 NumberInstructions(); | |
| 421 | |
| 422 const intptr_t block_count = postorder_.length(); | 420 const intptr_t block_count = postorder_.length(); |
| 423 ASSERT(postorder_[block_count - 1]->IsGraphEntry()); | 421 ASSERT(postorder_[block_count - 1]->IsGraphEntry()); |
| 424 for (intptr_t i = 0; i < (block_count - 1); i++) { | 422 for (intptr_t i = 0; i < (block_count - 1); i++) { |
| 425 BlockEntryInstr* block = postorder_[i]; | 423 BlockEntryInstr* block = postorder_[i]; |
| 426 | 424 |
| 427 // For every SSA value that is live out of this block, create an interval | 425 // For every SSA value that is live out of this block, create an interval |
| 428 // that covers the whole block. It will be shortened if we encounter a | 426 // that covers the whole block. It will be shortened if we encounter a |
| 429 // definition of this value in this block. | 427 // definition of this value in this block. |
| 430 for (BitVector::Iterator it(live_out_[i]); !it.Done(); it.Advance()) { | 428 for (BitVector::Iterator it(live_out_[i]); !it.Done(); it.Advance()) { |
| 431 LiveRange* range = GetLiveRange(it.Current()); | 429 LiveRange* range = GetLiveRange(it.Current()); |
| (...skipping 461 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 893 // numbers representing lifetime positions. Introduce explicit parallel | 891 // numbers representing lifetime positions. Introduce explicit parallel |
| 894 // move instructions in the predecessors of join nodes. The moves are used | 892 // move instructions in the predecessors of join nodes. The moves are used |
| 895 // for phi resolution. | 893 // for phi resolution. |
| 896 void FlowGraphAllocator::NumberInstructions() { | 894 void FlowGraphAllocator::NumberInstructions() { |
| 897 intptr_t pos = 0; | 895 intptr_t pos = 0; |
| 898 | 896 |
| 899 // The basic block order is reverse postorder. | 897 // The basic block order is reverse postorder. |
| 900 const intptr_t block_count = postorder_.length(); | 898 const intptr_t block_count = postorder_.length(); |
| 901 for (intptr_t i = block_count - 1; i >= 0; i--) { | 899 for (intptr_t i = block_count - 1; i >= 0; i--) { |
| 902 BlockEntryInstr* block = postorder_[i]; | 900 BlockEntryInstr* block = postorder_[i]; |
| 901 BlockInfo* info = new BlockInfo(block); | |
| 903 | 902 |
| 904 instructions_.Add(block); | 903 instructions_.Add(block); |
| 904 block_info_.Add(info); | |
| 905 block->set_start_pos(pos); | 905 block->set_start_pos(pos); |
| 906 block->set_lifetime_position(pos); | 906 block->set_lifetime_position(pos); |
| 907 pos += 2; | 907 pos += 2; |
| 908 | 908 |
| 909 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { | 909 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { |
| 910 Instruction* current = it.Current(); | 910 Instruction* current = it.Current(); |
| 911 // Do not assign numbers to parallel move instructions. | 911 // Do not assign numbers to parallel move instructions. |
| 912 if (!current->IsParallelMove()) { | 912 if (!current->IsParallelMove()) { |
| 913 instructions_.Add(current); | 913 instructions_.Add(current); |
| 914 block_info_.Add(info); | |
| 914 current->set_lifetime_position(pos); | 915 current->set_lifetime_position(pos); |
| 915 pos += 2; | 916 pos += 2; |
| 916 } | 917 } |
| 917 } | 918 } |
| 918 block->set_end_pos(pos); | 919 block->set_end_pos(pos); |
| 919 } | 920 } |
| 920 | 921 |
| 921 // Create parallel moves in join predecessors. This must be done after | 922 // Create parallel moves in join predecessors. This must be done after |
| 922 // all instructions are numbered. | 923 // all instructions are numbered. |
| 923 for (intptr_t i = block_count - 1; i >= 0; i--) { | 924 for (intptr_t i = block_count - 1; i >= 0; i--) { |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 940 // Populate the ParallelMove with empty moves. | 941 // Populate the ParallelMove with empty moves. |
| 941 for (intptr_t j = 0; j < phi_count; j++) { | 942 for (intptr_t j = 0; j < phi_count; j++) { |
| 942 move->AddMove(Location::NoLocation(), Location::NoLocation()); | 943 move->AddMove(Location::NoLocation(), Location::NoLocation()); |
| 943 } | 944 } |
| 944 } | 945 } |
| 945 } | 946 } |
| 946 } | 947 } |
| 947 } | 948 } |
| 948 | 949 |
| 949 | 950 |
| 951 static bool IsNestedLoop(BlockInfo* outer, BlockInfo* inner) { | |
|
srdjan
2012/08/02 20:04:37
Can you make checks a little bit more robust since
Vyacheslav Egorov (Google)
2012/08/03 06:29:00
Inlined function back into DiscoverLoops to avoid
| |
| 952 return (outer == NULL) || | |
| 953 (outer->entry()->block_id() < inner->entry()->block_id()); | |
| 954 } | |
| 955 | |
| 956 | |
| 957 // Discover structural (reducible) loops nesting structure. | |
|
srdjan
2012/08/02 20:04:37
Aren't all Dart generated loops reducible?
Vyacheslav Egorov (Google)
2012/08/03 06:29:00
AFAIK you can generate an irreducible control flow
srdjan
2012/08/03 21:40:49
You cannot do this. The break goes only to outer
| |
| 958 void FlowGraphAllocator::DiscoverLoops() { | |
| 959 // TODO(vegorov): consider using a generic algorithm to correctly discover | |
| 960 // both headers of reducible and irreducible loops. | |
| 961 BlockInfo* current_loop = NULL; | |
| 962 | |
| 963 const intptr_t block_count = postorder_.length(); | |
| 964 for (intptr_t i = 0; i < block_count; i++) { | |
| 965 BlockEntryInstr* block = postorder_[i]; | |
| 966 GotoInstr* goto_instr = block->last_instruction()->AsGoto(); | |
| 967 if (goto_instr != NULL) { | |
| 968 JoinEntryInstr* successor = goto_instr->successor(); | |
| 969 if (successor->postorder_number() > i) { | |
| 970 // This is back-edge. | |
| 971 BlockInfo* successor_info = BlockInfoAt(successor->lifetime_position()); | |
| 972 ASSERT(successor_info->entry() == successor); | |
| 973 if (!successor_info->is_loop_header() && | |
| 974 IsNestedLoop(current_loop, successor_info)) { | |
| 975 ASSERT(successor_info != current_loop); | |
| 976 | |
| 977 successor_info->mark_loop_header(); | |
| 978 // For loop header loop information points to the outer loop. | |
| 979 successor_info->set_loop(current_loop); | |
| 980 current_loop = successor_info; | |
| 981 } | |
| 982 } | |
| 983 } | |
| 984 | |
| 985 if (current_loop != NULL) { | |
| 986 BlockInfo* current_info = BlockInfoAt(block->lifetime_position()); | |
| 987 if (current_info == current_loop) { | |
| 988 ASSERT(current_loop->is_loop_header()); | |
| 989 current_loop = current_info->loop(); | |
| 990 } else { | |
| 991 current_info->set_loop(current_loop); | |
| 992 } | |
| 993 } | |
| 994 } | |
| 995 } | |
| 996 | |
| 997 | |
| 950 Instruction* FlowGraphAllocator::InstructionAt(intptr_t pos) const { | 998 Instruction* FlowGraphAllocator::InstructionAt(intptr_t pos) const { |
| 951 return instructions_[pos / 2]; | 999 return instructions_[pos / 2]; |
| 952 } | 1000 } |
| 953 | 1001 |
| 954 | 1002 |
| 1003 BlockInfo* FlowGraphAllocator::BlockInfoAt(intptr_t pos) const { | |
| 1004 return block_info_[pos / 2]; | |
| 1005 } | |
| 1006 | |
| 1007 | |
| 955 bool FlowGraphAllocator::IsBlockEntry(intptr_t pos) const { | 1008 bool FlowGraphAllocator::IsBlockEntry(intptr_t pos) const { |
| 956 return IsInstructionStartPosition(pos) && InstructionAt(pos)->IsBlockEntry(); | 1009 return IsInstructionStartPosition(pos) && InstructionAt(pos)->IsBlockEntry(); |
| 957 } | 1010 } |
| 958 | 1011 |
| 959 | 1012 |
| 960 void AllocationFinger::Initialize(LiveRange* range) { | 1013 void AllocationFinger::Initialize(LiveRange* range) { |
| 961 first_pending_use_interval_ = range->first_use_interval(); | 1014 first_pending_use_interval_ = range->first_use_interval(); |
| 962 first_register_use_ = range->first_use(); | 1015 first_register_use_ = range->first_use(); |
| 963 first_register_beneficial_use_ = range->first_use(); | 1016 first_register_beneficial_use_ = range->first_use(); |
| 964 first_hinted_use_ = range->first_use(); | 1017 first_hinted_use_ = range->first_use(); |
| (...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1131 | 1184 |
| 1132 last_use_interval_ = last_before_split; | 1185 last_use_interval_ = last_before_split; |
| 1133 last_use_interval_->next_ = NULL; | 1186 last_use_interval_->next_ = NULL; |
| 1134 return next_sibling_; | 1187 return next_sibling_; |
| 1135 } | 1188 } |
| 1136 | 1189 |
| 1137 | 1190 |
| 1138 LiveRange* FlowGraphAllocator::SplitBetween(LiveRange* range, | 1191 LiveRange* FlowGraphAllocator::SplitBetween(LiveRange* range, |
| 1139 intptr_t from, | 1192 intptr_t from, |
| 1140 intptr_t to) { | 1193 intptr_t to) { |
| 1141 // TODO(vegorov): select optimal split position based on loop structure. | |
| 1142 TRACE_ALLOC(("split %d [%d, %d) between [%d, %d)\n", | 1194 TRACE_ALLOC(("split %d [%d, %d) between [%d, %d)\n", |
| 1143 range->vreg(), range->Start(), range->End(), from, to)); | 1195 range->vreg(), range->Start(), range->End(), from, to)); |
| 1144 | 1196 |
| 1145 // Prefer spliting at instruction starts if possible. | 1197 intptr_t split_pos = kIllegalPosition; |
| 1146 if (from < ToInstructionStart(to)) { | 1198 |
| 1147 to = ToInstructionStart(to); | 1199 BlockInfo* split_block = BlockInfoAt(to); |
| 1200 if (from < split_block->entry()->lifetime_position()) { | |
| 1201 // Interval [from, to) spans multiple blocks. | |
| 1202 | |
| 1203 // If it covers any last block is inside a loop prefer splitting at | |
| 1204 // loops header if possible. | |
|
srdjan
2012/08/02 20:04:37
Please make comment easier to read.
Vyacheslav Egorov (Google)
2012/08/03 06:29:00
Done.
| |
| 1205 BlockInfo* loop_header = split_block->loop(); | |
| 1206 while ((loop_header != NULL) && | |
| 1207 (from < loop_header->entry()->lifetime_position())) { | |
| 1208 split_block = loop_header; | |
| 1209 loop_header = loop_header->loop(); | |
| 1210 } | |
| 1211 | |
| 1212 // Split at block's start. | |
| 1213 split_pos = split_block->entry()->lifetime_position(); | |
| 1214 } else { | |
| 1215 // Interval [from, to) is contained inside a single block. | |
| 1216 | |
| 1217 // Split at position corresponding to the end of the previous | |
| 1218 // instruction. | |
| 1219 split_pos = ToInstructionStart(to) - 1; | |
| 1148 } | 1220 } |
| 1149 | 1221 |
| 1150 // Splitting at the end is not allowed as it produces an empty | 1222 ASSERT((split_pos != kIllegalPosition) && (from < split_pos)); |
| 1151 // live range. | |
| 1152 if (to == range->End()) to -= 1; | |
| 1153 ASSERT(from <= to); | |
| 1154 | 1223 |
| 1155 return range->SplitAt(to); | 1224 return range->SplitAt(split_pos); |
| 1156 } | 1225 } |
| 1157 | 1226 |
| 1158 | 1227 |
| 1159 void FlowGraphAllocator::SpillBetween(LiveRange* range, | 1228 void FlowGraphAllocator::SpillBetween(LiveRange* range, |
| 1160 intptr_t from, | 1229 intptr_t from, |
| 1161 intptr_t to) { | 1230 intptr_t to) { |
| 1162 ASSERT(from < to); | 1231 ASSERT(from < to); |
| 1163 TRACE_ALLOC(("spill %d [%d, %d) between [%d, %d)\n", | 1232 TRACE_ALLOC(("spill %d [%d, %d) between [%d, %d)\n", |
| 1164 range->vreg(), range->Start(), range->End(), from, to)); | 1233 range->vreg(), range->Start(), range->End(), from, to)); |
| 1165 LiveRange* tail = range->SplitAt(from); | 1234 LiveRange* tail = range->SplitAt(from); |
| (...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1326 } | 1395 } |
| 1327 } | 1396 } |
| 1328 | 1397 |
| 1329 if (free_until < register_use->pos()) { | 1398 if (free_until < register_use->pos()) { |
| 1330 // Can't acquire free register. Spill until we really need one. | 1399 // Can't acquire free register. Spill until we really need one. |
| 1331 ASSERT(unallocated->Start() < ToInstructionStart(register_use->pos())); | 1400 ASSERT(unallocated->Start() < ToInstructionStart(register_use->pos())); |
| 1332 SpillBetween(unallocated, unallocated->Start(), register_use->pos()); | 1401 SpillBetween(unallocated, unallocated->Start(), register_use->pos()); |
| 1333 return; | 1402 return; |
| 1334 } | 1403 } |
| 1335 | 1404 |
| 1405 TRACE_ALLOC(("assigning blocked register %s to live range %d until %d\n", | |
| 1406 Location::RegisterLocation(candidate).Name(), | |
| 1407 unallocated->vreg(), | |
| 1408 blocked_at)); | |
| 1409 | |
| 1336 if (blocked_at < unallocated->End()) { | 1410 if (blocked_at < unallocated->End()) { |
| 1337 LiveRange* tail = SplitBetween(unallocated, | 1411 LiveRange* tail = SplitBetween(unallocated, |
| 1338 unallocated->Start(), | 1412 unallocated->Start(), |
| 1339 blocked_at); | 1413 blocked_at); |
| 1340 AddToUnallocated(tail); | 1414 AddToUnallocated(tail); |
| 1341 } | 1415 } |
| 1342 | 1416 |
| 1343 AssignNonFreeRegister(unallocated, candidate); | 1417 AssignNonFreeRegister(unallocated, candidate); |
| 1344 } | 1418 } |
| 1345 | 1419 |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1407 while (from < cpu_regs_[reg].length()) { | 1481 while (from < cpu_regs_[reg].length()) { |
| 1408 LiveRange* allocated = cpu_regs_[reg][from++]; | 1482 LiveRange* allocated = cpu_regs_[reg][from++]; |
| 1409 if (allocated != NULL) cpu_regs_[reg][to++] = allocated; | 1483 if (allocated != NULL) cpu_regs_[reg][to++] = allocated; |
| 1410 } | 1484 } |
| 1411 cpu_regs_[reg].TruncateTo(to); | 1485 cpu_regs_[reg].TruncateTo(to); |
| 1412 } | 1486 } |
| 1413 | 1487 |
| 1414 | 1488 |
| 1415 void FlowGraphAllocator::AssignNonFreeRegister(LiveRange* unallocated, | 1489 void FlowGraphAllocator::AssignNonFreeRegister(LiveRange* unallocated, |
| 1416 Register reg) { | 1490 Register reg) { |
| 1417 TRACE_ALLOC(("assigning blocked register %s to live range %d\n", | |
| 1418 Location::RegisterLocation(reg).Name(), | |
| 1419 unallocated->vreg())); | |
| 1420 | |
| 1421 intptr_t first_evicted = -1; | 1491 intptr_t first_evicted = -1; |
| 1422 for (intptr_t i = cpu_regs_[reg].length() - 1; i >= 0; i--) { | 1492 for (intptr_t i = cpu_regs_[reg].length() - 1; i >= 0; i--) { |
| 1423 LiveRange* allocated = cpu_regs_[reg][i]; | 1493 LiveRange* allocated = cpu_regs_[reg][i]; |
| 1424 if (allocated->vreg() < 0) continue; // Can't be evicted. | 1494 if (allocated->vreg() < 0) continue; // Can't be evicted. |
| 1425 if (EvictIntersection(allocated, unallocated)) { | 1495 if (EvictIntersection(allocated, unallocated)) { |
| 1426 ASSERT(allocated->End() <= unallocated->Start()); | 1496 // If allocated was not spilled convert all pending uses. |
| 1427 ConvertAllUses(allocated); | 1497 if (allocated->assigned_location().IsRegister()) { |
| 1498 ASSERT(allocated->End() <= unallocated->Start()); | |
| 1499 ConvertAllUses(allocated); | |
| 1500 } | |
| 1428 cpu_regs_[reg][i] = NULL; | 1501 cpu_regs_[reg][i] = NULL; |
| 1429 first_evicted = i; | 1502 first_evicted = i; |
| 1430 } | 1503 } |
| 1431 } | 1504 } |
| 1432 | 1505 |
| 1433 // Remove evicted ranges from the array. | 1506 // Remove evicted ranges from the array. |
| 1434 if (first_evicted != -1) RemoveEvicted(reg, first_evicted); | 1507 if (first_evicted != -1) RemoveEvicted(reg, first_evicted); |
| 1435 | 1508 |
| 1436 cpu_regs_[reg].Add(unallocated); | 1509 cpu_regs_[reg].Add(unallocated); |
| 1437 unallocated->set_assigned_location(Location::RegisterLocation(reg)); | 1510 unallocated->set_assigned_location(Location::RegisterLocation(reg)); |
| (...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1654 // Siblings were allocated to the same register. | 1727 // Siblings were allocated to the same register. |
| 1655 if (source.Equals(target)) return; | 1728 if (source.Equals(target)) return; |
| 1656 | 1729 |
| 1657 // Values are eagerly spilled. Spill slot already contains appropriate value. | 1730 // Values are eagerly spilled. Spill slot already contains appropriate value. |
| 1658 if (target.IsStackSlot()) { | 1731 if (target.IsStackSlot()) { |
| 1659 ASSERT(parent->spill_slot().Equals(target)); | 1732 ASSERT(parent->spill_slot().Equals(target)); |
| 1660 return; | 1733 return; |
| 1661 } | 1734 } |
| 1662 | 1735 |
| 1663 Instruction* last = source_block->last_instruction(); | 1736 Instruction* last = source_block->last_instruction(); |
| 1664 if (last->SuccessorCount() == 1) { | 1737 if ((last->SuccessorCount() == 1) && !source_block->IsGraphEntry()) { |
| 1665 ASSERT(last->IsGoto()); | 1738 ASSERT(last->IsGoto()); |
| 1666 last->AsGoto()->GetParallelMove()->AddMove(target, source); | 1739 last->AsGoto()->GetParallelMove()->AddMove(target, source); |
| 1667 } else { | 1740 } else { |
| 1668 target_block->GetParallelMove()->AddMove(target, source); | 1741 target_block->GetParallelMove()->AddMove(target, source); |
| 1669 } | 1742 } |
| 1670 } | 1743 } |
| 1671 | 1744 |
| 1672 | 1745 |
| 1673 void FlowGraphAllocator::ResolveControlFlow() { | 1746 void FlowGraphAllocator::ResolveControlFlow() { |
| 1674 // Resolve linear control flow between touching split siblings | 1747 // Resolve linear control flow between touching split siblings |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1722 } | 1795 } |
| 1723 } | 1796 } |
| 1724 } | 1797 } |
| 1725 | 1798 |
| 1726 | 1799 |
| 1727 void FlowGraphAllocator::AllocateRegisters() { | 1800 void FlowGraphAllocator::AllocateRegisters() { |
| 1728 EliminateEnvironmentUses(); | 1801 EliminateEnvironmentUses(); |
| 1729 | 1802 |
| 1730 AnalyzeLiveness(); | 1803 AnalyzeLiveness(); |
| 1731 | 1804 |
| 1805 NumberInstructions(); | |
| 1806 | |
| 1807 DiscoverLoops(); | |
| 1808 | |
| 1732 BuildLiveRanges(); | 1809 BuildLiveRanges(); |
| 1733 | 1810 |
| 1734 if (FLAG_print_ssa_liveness) { | 1811 if (FLAG_print_ssa_liveness) { |
| 1735 DumpLiveness(); | 1812 DumpLiveness(); |
| 1736 } | 1813 } |
| 1737 | 1814 |
| 1738 AllocateCPURegisters(); | 1815 AllocateCPURegisters(); |
| 1739 | 1816 |
| 1740 ResolveControlFlow(); | 1817 ResolveControlFlow(); |
| 1741 | 1818 |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 1754 OS::Print("-- [after ssa allocator] ir [%s] -------------\n", | 1831 OS::Print("-- [after ssa allocator] ir [%s] -------------\n", |
| 1755 function.ToFullyQualifiedCString()); | 1832 function.ToFullyQualifiedCString()); |
| 1756 FlowGraphPrinter printer(Function::Handle(), block_order_, true); | 1833 FlowGraphPrinter printer(Function::Handle(), block_order_, true); |
| 1757 printer.PrintBlocks(); | 1834 printer.PrintBlocks(); |
| 1758 OS::Print("----------------------------------------------\n"); | 1835 OS::Print("----------------------------------------------\n"); |
| 1759 } | 1836 } |
| 1760 } | 1837 } |
| 1761 | 1838 |
| 1762 | 1839 |
| 1763 } // namespace dart | 1840 } // namespace dart |
| OLD | NEW |