Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 the V8 project authors. All rights reserved. | 1 // Copyright 2014 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/base/adapters.h" | 5 #include "src/base/adapters.h" |
| 6 #include "src/compiler/linkage.h" | 6 #include "src/compiler/linkage.h" |
| 7 #include "src/compiler/register-allocator.h" | 7 #include "src/compiler/register-allocator.h" |
| 8 #include "src/string-stream.h" | 8 #include "src/string-stream.h" |
| 9 | 9 |
| 10 namespace v8 { | 10 namespace v8 { |
| 11 namespace internal { | 11 namespace internal { |
| 12 namespace compiler { | 12 namespace compiler { |
| 13 | 13 |
| 14 #define TRACE(...) \ | 14 #define TRACE(...) \ |
| 15 do { \ | 15 do { \ |
| 16 if (FLAG_trace_alloc) PrintF(__VA_ARGS__); \ | 16 if (FLAG_trace_alloc) PrintF(__VA_ARGS__); \ |
| 17 } while (false) | 17 } while (false) |
| 18 | 18 |
| 19 | |
| 20 class CoallescedLiveRanges : public ZoneObject { | |
| 21 public: | |
| 22 explicit CoallescedLiveRanges(Zone* zone) : storage_(zone) {} | |
| 23 | |
| 24 LiveRange* Find(UseInterval* query) { | |
| 25 ZoneSplayTree<Config>::Locator locator; | |
| 26 | |
| 27 if (storage_.Find(GetKey(query), &locator)) { | |
| 28 return locator.value(); | |
| 29 } | |
| 30 return nullptr; | |
| 31 } | |
| 32 | |
| 33 // TODO(mtrofin): Change to void returning if we do not care if the interval | |
| 34 // was previously added. | |
| 35 bool Insert(LiveRange* range) { | |
| 36 auto* interval = range->first_interval(); | |
| 37 while (interval != nullptr) { | |
| 38 if (!Insert(interval, range)) return false; | |
| 39 interval = interval->next(); | |
| 40 } | |
| 41 return true; | |
| 42 } | |
| 43 | |
| 44 bool Remove(LiveRange* range) { | |
| 45 bool ret = false; | |
| 46 auto* segment = range->first_interval(); | |
| 47 while (segment != nullptr) { | |
| 48 ret |= Remove(segment); | |
| 49 segment = segment->next(); | |
| 50 } | |
| 51 return ret; | |
| 52 } | |
| 53 | |
| 54 bool IsEmpty() { return storage_.is_empty(); } | |
| 55 | |
| 56 private: | |
| 57 struct Config { | |
| 58 typedef std::pair<int, int> Key; | |
| 59 typedef LiveRange* Value; | |
| 60 static const Key kNoKey; | |
| 61 static Value NoValue() { return nullptr; } | |
| 62 static int Compare(const Key& a, const Key& b) { | |
| 63 if (a.second <= b.first) return -1; | |
| 64 if (a.first >= b.second) return 1; | |
| 65 return 0; | |
| 66 } | |
| 67 }; | |
| 68 | |
| 69 Config::Key GetKey(UseInterval* interval) { | |
| 70 if (interval == nullptr) return std::make_pair(0, 0); | |
| 71 return std::make_pair(interval->start().Value(), interval->end().Value()); | |
| 72 } | |
| 73 | |
| 74 // TODO(mtrofin): Change to void returning if we do not care if the interval | |
| 75 // was previously added. | |
| 76 bool Insert(UseInterval* interval, LiveRange* range) { | |
| 77 ZoneSplayTree<Config>::Locator locator; | |
| 78 bool ret = storage_.Insert(GetKey(interval), &locator); | |
| 79 if (ret) locator.set_value(range); | |
| 80 return ret; | |
| 81 } | |
| 82 | |
| 83 bool Remove(UseInterval* key) { return storage_.Remove(GetKey(key)); } | |
| 84 | |
| 85 ZoneSplayTree<Config> storage_; | |
| 86 DISALLOW_COPY_AND_ASSIGN(CoallescedLiveRanges); | |
| 87 }; | |
| 88 | |
| 89 | |
| 90 const std::pair<int, int> CoallescedLiveRanges::Config::kNoKey = | |
| 91 std::make_pair<int, int>(0, 0); | |
| 92 | |
| 93 | |
| 19 namespace { | 94 namespace { |
| 20 | 95 |
| 21 inline LifetimePosition Min(LifetimePosition a, LifetimePosition b) { | 96 inline LifetimePosition Min(LifetimePosition a, LifetimePosition b) { |
| 22 return a.Value() < b.Value() ? a : b; | 97 return a.Value() < b.Value() ? a : b; |
| 23 } | 98 } |
| 24 | 99 |
| 25 | 100 |
| 26 inline LifetimePosition Max(LifetimePosition a, LifetimePosition b) { | 101 inline LifetimePosition Max(LifetimePosition a, LifetimePosition b) { |
| 27 return a.Value() > b.Value() ? a : b; | 102 return a.Value() > b.Value() ? a : b; |
| 28 } | 103 } |
| 29 | 104 |
| 30 | 105 |
| 31 void RemoveElement(ZoneVector<LiveRange*>* v, LiveRange* range) { | 106 void RemoveElement(ZoneVector<LiveRange*>* v, LiveRange* range) { |
| 32 auto it = std::find(v->begin(), v->end(), range); | 107 auto it = std::find(v->begin(), v->end(), range); |
| 33 DCHECK(it != v->end()); | 108 DCHECK(it != v->end()); |
| 34 v->erase(it); | 109 v->erase(it); |
| 35 } | 110 } |
| 36 | 111 |
| 112 | |
| 113 static int GetRegisterCount(const RegisterConfiguration* cfg, | |
| 114 RegisterKind kind) { | |
| 115 return kind == DOUBLE_REGISTERS ? cfg->num_aliased_double_registers() | |
| 116 : cfg->num_general_registers(); | |
| 117 } | |
| 118 | |
| 119 | |
| 120 static const ZoneVector<LiveRange*>& GetFixedRegisters( | |
| 121 const RegisterAllocationData* data, RegisterKind kind) { | |
| 122 return kind == DOUBLE_REGISTERS ? data->fixed_double_live_ranges() | |
| 123 : data->fixed_live_ranges(); | |
| 124 } | |
| 125 | |
| 37 } // namespace | 126 } // namespace |
| 38 | 127 |
| 39 | 128 |
| 40 UsePosition::UsePosition(LifetimePosition pos, InstructionOperand* operand, | 129 UsePosition::UsePosition(LifetimePosition pos, InstructionOperand* operand, |
| 41 InstructionOperand* hint) | 130 InstructionOperand* hint) |
| 42 : operand_(operand), hint_(hint), pos_(pos), next_(nullptr), flags_(0) { | 131 : operand_(operand), hint_(hint), pos_(pos), next_(nullptr), flags_(0) { |
| 43 bool register_beneficial = true; | 132 bool register_beneficial = true; |
| 44 UsePositionType type = UsePositionType::kAny; | 133 UsePositionType type = UsePositionType::kAny; |
| 45 if (operand_ != nullptr && operand_->IsUnallocated()) { | 134 if (operand_ != nullptr && operand_->IsUnallocated()) { |
| 46 const UnallocatedOperand* unalloc = UnallocatedOperand::cast(operand_); | 135 const UnallocatedOperand* unalloc = UnallocatedOperand::cast(operand_); |
| (...skipping 1377 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1424 for (auto current : data()->live_ranges()) { | 1513 for (auto current : data()->live_ranges()) { |
| 1425 if (current != nullptr) current->Verify(); | 1514 if (current != nullptr) current->Verify(); |
| 1426 } | 1515 } |
| 1427 } | 1516 } |
| 1428 | 1517 |
| 1429 | 1518 |
| 1430 LinearScanAllocator::LinearScanAllocator(RegisterAllocationData* data, | 1519 LinearScanAllocator::LinearScanAllocator(RegisterAllocationData* data, |
| 1431 RegisterKind kind) | 1520 RegisterKind kind) |
| 1432 : data_(data), | 1521 : data_(data), |
| 1433 mode_(kind), | 1522 mode_(kind), |
| 1434 num_registers_(kind == DOUBLE_REGISTERS | 1523 num_registers_(GetRegisterCount(data->config(), kind)), |
| 1435 ? config()->num_aliased_double_registers() | |
| 1436 : config()->num_general_registers()), | |
| 1437 unhandled_live_ranges_(allocation_zone()), | 1524 unhandled_live_ranges_(allocation_zone()), |
| 1438 active_live_ranges_(allocation_zone()), | 1525 active_live_ranges_(allocation_zone()), |
| 1439 inactive_live_ranges_(allocation_zone()) { | 1526 inactive_live_ranges_(allocation_zone()) { |
| 1440 unhandled_live_ranges().reserve( | 1527 unhandled_live_ranges().reserve( |
| 1441 static_cast<size_t>(code()->VirtualRegisterCount() * 2)); | 1528 static_cast<size_t>(code()->VirtualRegisterCount() * 2)); |
| 1442 active_live_ranges().reserve(8); | 1529 active_live_ranges().reserve(8); |
| 1443 inactive_live_ranges().reserve(8); | 1530 inactive_live_ranges().reserve(8); |
| 1444 // TryAllocateFreeReg and AllocateBlockedReg assume this | 1531 // TryAllocateFreeReg and AllocateBlockedReg assume this |
| 1445 // when allocating local arrays. | 1532 // when allocating local arrays. |
| 1446 DCHECK(RegisterConfiguration::kMaxDoubleRegisters >= | 1533 DCHECK(RegisterConfiguration::kMaxDoubleRegisters >= |
| 1447 config()->num_general_registers()); | 1534 config()->num_general_registers()); |
| 1448 } | 1535 } |
| 1449 | 1536 |
| 1450 | 1537 |
| 1451 void LinearScanAllocator::AllocateRegisters() { | 1538 void LinearScanAllocator::AllocateRegisters() { |
| 1452 DCHECK(unhandled_live_ranges().empty()); | 1539 DCHECK(unhandled_live_ranges().empty()); |
| 1453 | 1540 |
| 1454 for (auto range : live_ranges()) { | 1541 for (auto range : live_ranges()) { |
| 1455 if (range == nullptr) continue; | 1542 if (range == nullptr) continue; |
| 1456 if (range->Kind() == mode_) { | 1543 if (range->Kind() == mode_) { |
| 1457 AddToUnhandledUnsorted(range); | 1544 AddToUnhandledUnsorted(range); |
| 1458 } | 1545 } |
| 1459 } | 1546 } |
| 1460 SortUnhandled(); | 1547 SortUnhandled(); |
| 1461 DCHECK(UnhandledIsSorted()); | 1548 DCHECK(UnhandledIsSorted()); |
| 1462 | 1549 |
| 1463 DCHECK(active_live_ranges().empty()); | 1550 DCHECK(active_live_ranges().empty()); |
| 1464 DCHECK(inactive_live_ranges().empty()); | 1551 DCHECK(inactive_live_ranges().empty()); |
| 1465 | 1552 |
| 1466 if (mode_ == DOUBLE_REGISTERS) { | 1553 auto& fixed_ranges = GetFixedRegisters(data(), mode_); |
| 1467 for (auto current : fixed_double_live_ranges()) { | 1554 for (auto current : fixed_ranges) { |
| 1468 if (current != nullptr) AddToInactive(current); | 1555 if (current != nullptr) { |
| 1469 } | 1556 DCHECK_EQ(mode_, current->Kind()); |
| 1470 } else { | 1557 AddToInactive(current); |
| 1471 DCHECK(mode_ == GENERAL_REGISTERS); | |
| 1472 for (auto current : fixed_live_ranges()) { | |
| 1473 if (current != nullptr) AddToInactive(current); | |
| 1474 } | 1558 } |
| 1475 } | 1559 } |
| 1476 | 1560 |
| 1477 while (!unhandled_live_ranges().empty()) { | 1561 while (!unhandled_live_ranges().empty()) { |
| 1478 DCHECK(UnhandledIsSorted()); | 1562 DCHECK(UnhandledIsSorted()); |
| 1479 auto current = unhandled_live_ranges().back(); | 1563 auto current = unhandled_live_ranges().back(); |
| 1480 unhandled_live_ranges().pop_back(); | 1564 unhandled_live_ranges().pop_back(); |
| 1481 DCHECK(UnhandledIsSorted()); | 1565 DCHECK(UnhandledIsSorted()); |
| 1482 auto position = current->Start(); | 1566 auto position = current->Start(); |
| 1483 #ifdef DEBUG | 1567 #ifdef DEBUG |
| 1484 allocation_finger_ = position; | 1568 allocation_finger_ = position; |
| 1485 #endif | 1569 #endif |
| 1486 TRACE("Processing interval %d start=%d\n", current->id(), position.Value()); | 1570 TRACE("Processing interval %d start=%d\n", current->id(), position.Value()); |
| 1487 | 1571 |
| 1488 if (!current->HasNoSpillType()) { | 1572 if (!current->HasNoSpillType()) { |
| 1489 TRACE("Live range %d already has a spill operand\n", current->id()); | 1573 TRACE("Live range %d already has a spill operand\n", current->id()); |
| 1490 auto next_pos = position; | 1574 auto next_pos = position; |
| 1491 if (next_pos.IsGapPosition()) { | 1575 if (next_pos.IsGapPosition()) { |
| 1492 next_pos = next_pos.NextStart(); | 1576 next_pos = next_pos.NextStart(); |
| 1493 } | 1577 } |
| 1494 auto pos = current->NextUsePositionRegisterIsBeneficial(next_pos); | 1578 auto pos = current->NextUsePositionRegisterIsBeneficial(next_pos); |
| 1495 // If the range already has a spill operand and it doesn't need a | 1579 // If the range already has a spill operand and it doesn't need a |
| 1496 // register immediately, split it and spill the first part of the range. | 1580 // register immediately, split it and spill the first part of the range. |
| 1497 if (pos == nullptr) { | 1581 if (pos == nullptr) { |
| 1498 Spill(current); | 1582 data()->Spill(current); |
| 1499 continue; | 1583 continue; |
| 1500 } else if (pos->pos().Value() > current->Start().NextStart().Value()) { | 1584 } else if (pos->pos().Value() > current->Start().NextStart().Value()) { |
| 1501 // Do not spill live range eagerly if use position that can benefit from | 1585 // Do not spill live range eagerly if use position that can benefit from |
| 1502 // the register is too close to the start of live range. | 1586 // the register is too close to the start of live range. |
| 1503 SpillBetween(current, current->Start(), pos->pos()); | 1587 SpillBetween(current, current->Start(), pos->pos()); |
| 1504 DCHECK(UnhandledIsSorted()); | 1588 DCHECK(UnhandledIsSorted()); |
| 1505 continue; | 1589 continue; |
| 1506 } | 1590 } |
| 1507 } | 1591 } |
| 1508 | 1592 |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1695 auto pos = free_until_pos[reg]; | 1779 auto pos = free_until_pos[reg]; |
| 1696 | 1780 |
| 1697 if (pos.Value() <= current->Start().Value()) { | 1781 if (pos.Value() <= current->Start().Value()) { |
| 1698 // All registers are blocked. | 1782 // All registers are blocked. |
| 1699 return false; | 1783 return false; |
| 1700 } | 1784 } |
| 1701 | 1785 |
| 1702 if (pos.Value() < current->End().Value()) { | 1786 if (pos.Value() < current->End().Value()) { |
| 1703 // Register reg is available at the range start but becomes blocked before | 1787 // Register reg is available at the range start but becomes blocked before |
| 1704 // the range end. Split current at position where it becomes blocked. | 1788 // the range end. Split current at position where it becomes blocked. |
| 1705 auto tail = SplitRangeAt(current, pos); | 1789 auto tail = data()->SplitRangeAt(current, pos); |
| 1706 AddToUnhandledSorted(tail); | 1790 AddToUnhandledSorted(tail); |
| 1707 } | 1791 } |
| 1708 | 1792 |
| 1709 // Register reg is available at the range start and is free until | 1793 // Register reg is available at the range start and is free until |
| 1710 // the range end. | 1794 // the range end. |
| 1711 DCHECK(pos.Value() >= current->End().Value()); | 1795 DCHECK(pos.Value() >= current->End().Value()); |
| 1712 TRACE("Assigning free reg %s to live range %d\n", RegisterName(reg), | 1796 TRACE("Assigning free reg %s to live range %d\n", RegisterName(reg), |
| 1713 current->id()); | 1797 current->id()); |
| 1714 SetLiveRangeAssignedRegister(current, reg); | 1798 SetLiveRangeAssignedRegister(current, reg); |
| 1715 | 1799 |
| 1716 return true; | 1800 return true; |
| 1717 } | 1801 } |
| 1718 | 1802 |
| 1719 | 1803 |
| 1720 void LinearScanAllocator::AllocateBlockedReg(LiveRange* current) { | 1804 void LinearScanAllocator::AllocateBlockedReg(LiveRange* current) { |
| 1721 auto register_use = current->NextRegisterPosition(current->Start()); | 1805 auto register_use = current->NextRegisterPosition(current->Start()); |
| 1722 if (register_use == nullptr) { | 1806 if (register_use == nullptr) { |
| 1723 // There is no use in the current live range that requires a register. | 1807 // There is no use in the current live range that requires a register. |
| 1724 // We can just spill it. | 1808 // We can just spill it. |
| 1725 Spill(current); | 1809 data()->Spill(current); |
| 1726 return; | 1810 return; |
| 1727 } | 1811 } |
| 1728 | 1812 |
| 1729 LifetimePosition use_pos[RegisterConfiguration::kMaxDoubleRegisters]; | 1813 LifetimePosition use_pos[RegisterConfiguration::kMaxDoubleRegisters]; |
| 1730 LifetimePosition block_pos[RegisterConfiguration::kMaxDoubleRegisters]; | 1814 LifetimePosition block_pos[RegisterConfiguration::kMaxDoubleRegisters]; |
| 1731 | 1815 |
| 1732 for (int i = 0; i < num_registers_; i++) { | 1816 for (int i = 0; i < num_registers_; i++) { |
| 1733 use_pos[i] = block_pos[i] = LifetimePosition::MaxPosition(); | 1817 use_pos[i] = block_pos[i] = LifetimePosition::MaxPosition(); |
| 1734 } | 1818 } |
| 1735 | 1819 |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1775 // All registers are blocked before the first use that requires a register. | 1859 // All registers are blocked before the first use that requires a register. |
| 1776 // Spill starting part of live range up to that use. | 1860 // Spill starting part of live range up to that use. |
| 1777 SpillBetween(current, current->Start(), register_use->pos()); | 1861 SpillBetween(current, current->Start(), register_use->pos()); |
| 1778 return; | 1862 return; |
| 1779 } | 1863 } |
| 1780 | 1864 |
| 1781 if (block_pos[reg].Value() < current->End().Value()) { | 1865 if (block_pos[reg].Value() < current->End().Value()) { |
| 1782 // Register becomes blocked before the current range end. Split before that | 1866 // Register becomes blocked before the current range end. Split before that |
| 1783 // position. | 1867 // position. |
| 1784 LiveRange* tail = | 1868 LiveRange* tail = |
| 1785 SplitBetween(current, current->Start(), block_pos[reg].Start()); | 1869 data()->SplitBetween(current, current->Start(), block_pos[reg].Start()); |
| 1786 AddToUnhandledSorted(tail); | 1870 AddToUnhandledSorted(tail); |
| 1787 } | 1871 } |
| 1788 | 1872 |
| 1789 // Register reg is not blocked for the whole range. | 1873 // Register reg is not blocked for the whole range. |
| 1790 DCHECK(block_pos[reg].Value() >= current->End().Value()); | 1874 DCHECK(block_pos[reg].Value() >= current->End().Value()); |
| 1791 TRACE("Assigning blocked reg %s to live range %d\n", RegisterName(reg), | 1875 TRACE("Assigning blocked reg %s to live range %d\n", RegisterName(reg), |
| 1792 current->id()); | 1876 current->id()); |
| 1793 SetLiveRangeAssignedRegister(current, reg); | 1877 SetLiveRangeAssignedRegister(current, reg); |
| 1794 | 1878 |
| 1795 // This register was not free. Thus we need to find and spill | 1879 // This register was not free. Thus we need to find and spill |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1949 auto next_pos = range->Start(); | 2033 auto next_pos = range->Start(); |
| 1950 if (next_pos.IsGapPosition()) next_pos = next_pos.NextStart(); | 2034 if (next_pos.IsGapPosition()) next_pos = next_pos.NextStart(); |
| 1951 auto pos = range->NextUsePositionRegisterIsBeneficial(next_pos); | 2035 auto pos = range->NextUsePositionRegisterIsBeneficial(next_pos); |
| 1952 if (pos == nullptr) { | 2036 if (pos == nullptr) { |
| 1953 auto spill_range = | 2037 auto spill_range = |
| 1954 range->TopLevel()->HasSpillRange() | 2038 range->TopLevel()->HasSpillRange() |
| 1955 ? range->TopLevel()->GetSpillRange() | 2039 ? range->TopLevel()->GetSpillRange() |
| 1956 : data()->AssignSpillRangeToLiveRange(range->TopLevel()); | 2040 : data()->AssignSpillRangeToLiveRange(range->TopLevel()); |
| 1957 bool merged = first_op_spill->TryMerge(spill_range); | 2041 bool merged = first_op_spill->TryMerge(spill_range); |
| 1958 CHECK(merged); | 2042 CHECK(merged); |
| 1959 Spill(range); | 2043 data()->Spill(range); |
| 1960 return true; | 2044 return true; |
| 1961 } else if (pos->pos().Value() > range->Start().NextStart().Value()) { | 2045 } else if (pos->pos().Value() > range->Start().NextStart().Value()) { |
| 1962 auto spill_range = | 2046 auto spill_range = |
| 1963 range->TopLevel()->HasSpillRange() | 2047 range->TopLevel()->HasSpillRange() |
| 1964 ? range->TopLevel()->GetSpillRange() | 2048 ? range->TopLevel()->GetSpillRange() |
| 1965 : data()->AssignSpillRangeToLiveRange(range->TopLevel()); | 2049 : data()->AssignSpillRangeToLiveRange(range->TopLevel()); |
| 1966 bool merged = first_op_spill->TryMerge(spill_range); | 2050 bool merged = first_op_spill->TryMerge(spill_range); |
| 1967 CHECK(merged); | 2051 CHECK(merged); |
| 1968 SpillBetween(range, range->Start(), pos->pos()); | 2052 SpillBetween(range, range->Start(), pos->pos()); |
| 1969 DCHECK(UnhandledIsSorted()); | 2053 DCHECK(UnhandledIsSorted()); |
| 1970 return true; | 2054 return true; |
| 1971 } | 2055 } |
| 1972 return false; | 2056 return false; |
| 1973 } | 2057 } |
| 1974 | 2058 |
| 1975 | 2059 |
| 1976 LiveRange* LinearScanAllocator::SplitRangeAt(LiveRange* range, | 2060 LiveRange* RegisterAllocationData::SplitRangeAt(LiveRange* range, |
| 1977 LifetimePosition pos) { | 2061 LifetimePosition pos) { |
| 1978 DCHECK(!range->IsFixed()); | 2062 DCHECK(!range->IsFixed()); |
| 1979 TRACE("Splitting live range %d at %d\n", range->id(), pos.Value()); | 2063 TRACE("Splitting live range %d at %d\n", range->id(), pos.Value()); |
| 1980 | 2064 |
| 1981 if (pos.Value() <= range->Start().Value()) return range; | 2065 if (pos.Value() <= range->Start().Value()) return range; |
| 1982 | 2066 |
| 1983 // We can't properly connect liveranges if splitting occurred at the end | 2067 // We can't properly connect liveranges if splitting occurred at the end |
| 1984 // a block. | 2068 // a block. |
| 1985 DCHECK(pos.IsStart() || pos.IsGapPosition() || | 2069 DCHECK(pos.IsStart() || pos.IsGapPosition() || |
| 1986 (code()->GetInstructionBlock(pos.ToInstructionIndex())) | 2070 (code()->GetInstructionBlock(pos.ToInstructionIndex())) |
| 1987 ->last_instruction_index() != pos.ToInstructionIndex()); | 2071 ->last_instruction_index() != pos.ToInstructionIndex()); |
| 1988 | 2072 |
| 1989 int vreg = GetVirtualRegister(); | 2073 int vreg = GetVirtualRegister(); |
| 1990 auto result = LiveRangeFor(vreg); | 2074 auto result = LiveRangeFor(vreg); |
| 1991 range->SplitAt(pos, result, allocation_zone()); | 2075 range->SplitAt(pos, result, allocation_zone()); |
| 1992 return result; | 2076 return result; |
| 1993 } | 2077 } |
| 1994 | 2078 |
| 1995 | 2079 |
| 1996 LiveRange* LinearScanAllocator::SplitBetween(LiveRange* range, | 2080 LiveRange* RegisterAllocationData::SplitBetween(LiveRange* range, |
| 1997 LifetimePosition start, | 2081 LifetimePosition start, |
| 1998 LifetimePosition end) { | 2082 LifetimePosition end) { |
| 1999 DCHECK(!range->IsFixed()); | 2083 DCHECK(!range->IsFixed()); |
| 2000 TRACE("Splitting live range %d in position between [%d, %d]\n", range->id(), | 2084 TRACE("Splitting live range %d in position between [%d, %d]\n", range->id(), |
| 2001 start.Value(), end.Value()); | 2085 start.Value(), end.Value()); |
| 2002 | 2086 |
| 2003 auto split_pos = FindOptimalSplitPos(start, end); | 2087 auto split_pos = FindOptimalSplitPos(start, end); |
| 2004 DCHECK(split_pos.Value() >= start.Value()); | 2088 DCHECK(split_pos.Value() >= start.Value()); |
| 2005 return SplitRangeAt(range, split_pos); | 2089 return SplitRangeAt(range, split_pos); |
| 2006 } | 2090 } |
| 2007 | 2091 |
| 2008 | 2092 |
| 2009 LifetimePosition LinearScanAllocator::FindOptimalSplitPos( | 2093 LifetimePosition RegisterAllocationData::FindOptimalSplitPos( |
| 2010 LifetimePosition start, LifetimePosition end) { | 2094 LifetimePosition start, LifetimePosition end) { |
| 2011 int start_instr = start.ToInstructionIndex(); | 2095 int start_instr = start.ToInstructionIndex(); |
| 2012 int end_instr = end.ToInstructionIndex(); | 2096 int end_instr = end.ToInstructionIndex(); |
| 2013 DCHECK(start_instr <= end_instr); | 2097 DCHECK(start_instr <= end_instr); |
| 2014 | 2098 |
| 2015 // We have no choice | 2099 // We have no choice |
| 2016 if (start_instr == end_instr) return end; | 2100 if (start_instr == end_instr) return end; |
| 2017 | 2101 |
| 2018 auto start_block = GetInstructionBlock(start); | 2102 auto start_block = GetInstructionBlock(start); |
| 2019 auto end_block = GetInstructionBlock(end); | 2103 auto end_block = GetInstructionBlock(end); |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 2036 // We did not find any suitable outer loop. Split at the latest possible | 2120 // We did not find any suitable outer loop. Split at the latest possible |
| 2037 // position unless end_block is a loop header itself. | 2121 // position unless end_block is a loop header itself. |
| 2038 if (block == end_block && !end_block->IsLoopHeader()) return end; | 2122 if (block == end_block && !end_block->IsLoopHeader()) return end; |
| 2039 | 2123 |
| 2040 return LifetimePosition::GapFromInstructionIndex( | 2124 return LifetimePosition::GapFromInstructionIndex( |
| 2041 block->first_instruction_index()); | 2125 block->first_instruction_index()); |
| 2042 } | 2126 } |
| 2043 | 2127 |
| 2044 | 2128 |
| 2045 void LinearScanAllocator::SpillAfter(LiveRange* range, LifetimePosition pos) { | 2129 void LinearScanAllocator::SpillAfter(LiveRange* range, LifetimePosition pos) { |
| 2046 auto second_part = SplitRangeAt(range, pos); | 2130 auto second_part = data()->SplitRangeAt(range, pos); |
| 2047 Spill(second_part); | 2131 data()->Spill(second_part); |
| 2048 } | 2132 } |
| 2049 | 2133 |
| 2050 | 2134 |
| 2051 void LinearScanAllocator::SpillBetween(LiveRange* range, LifetimePosition start, | 2135 void LinearScanAllocator::SpillBetween(LiveRange* range, LifetimePosition start, |
| 2052 LifetimePosition end) { | 2136 LifetimePosition end) { |
| 2053 SpillBetweenUntil(range, start, start, end); | 2137 SpillBetweenUntil(range, start, start, end); |
| 2054 } | 2138 } |
| 2055 | 2139 |
| 2056 | 2140 |
| 2057 void LinearScanAllocator::SpillBetweenUntil(LiveRange* range, | 2141 void LinearScanAllocator::SpillBetweenUntil(LiveRange* range, |
| 2058 LifetimePosition start, | 2142 LifetimePosition start, |
| 2059 LifetimePosition until, | 2143 LifetimePosition until, |
| 2060 LifetimePosition end) { | 2144 LifetimePosition end) { |
| 2061 CHECK(start.Value() < end.Value()); | 2145 CHECK(start.Value() < end.Value()); |
| 2062 auto second_part = SplitRangeAt(range, start); | 2146 auto second_part = data()->SplitRangeAt(range, start); |
| 2063 | 2147 |
| 2064 if (second_part->Start().Value() < end.Value()) { | 2148 if (second_part->Start().Value() < end.Value()) { |
| 2065 // The split result intersects with [start, end[. | 2149 // The split result intersects with [start, end[. |
| 2066 // Split it at position between ]start+1, end[, spill the middle part | 2150 // Split it at position between ]start+1, end[, spill the middle part |
| 2067 // and put the rest to unhandled. | 2151 // and put the rest to unhandled. |
| 2068 auto third_part_end = end.PrevStart().End(); | 2152 auto third_part_end = end.PrevStart().End(); |
| 2069 if (data()->IsBlockBoundary(end.Start())) { | 2153 if (data()->IsBlockBoundary(end.Start())) { |
| 2070 third_part_end = end.Start(); | 2154 third_part_end = end.Start(); |
| 2071 } | 2155 } |
| 2072 auto third_part = SplitBetween( | 2156 auto third_part = data()->SplitBetween( |
| 2073 second_part, Max(second_part->Start().End(), until), third_part_end); | 2157 second_part, Max(second_part->Start().End(), until), third_part_end); |
| 2074 | 2158 |
| 2075 DCHECK(third_part != second_part); | 2159 DCHECK(third_part != second_part); |
| 2076 | 2160 |
| 2077 Spill(second_part); | 2161 data()->Spill(second_part); |
| 2078 AddToUnhandledSorted(third_part); | 2162 AddToUnhandledSorted(third_part); |
| 2079 } else { | 2163 } else { |
| 2080 // The split result does not intersect with [start, end[. | 2164 // The split result does not intersect with [start, end[. |
| 2081 // Nothing to spill. Just put it to unhandled as whole. | 2165 // Nothing to spill. Just put it to unhandled as whole. |
| 2082 AddToUnhandledSorted(second_part); | 2166 AddToUnhandledSorted(second_part); |
| 2083 } | 2167 } |
| 2084 } | 2168 } |
| 2085 | 2169 |
| 2086 | 2170 |
| 2087 void LinearScanAllocator::Spill(LiveRange* range) { | 2171 void RegisterAllocationData::Spill(LiveRange* range) { |
| 2088 DCHECK(!range->IsSpilled()); | 2172 DCHECK(!range->IsSpilled()); |
| 2089 TRACE("Spilling live range %d\n", range->id()); | 2173 TRACE("Spilling live range %d\n", range->id()); |
| 2090 auto first = range->TopLevel(); | 2174 auto first = range->TopLevel(); |
| 2091 if (first->HasNoSpillType()) { | 2175 if (first->HasNoSpillType()) { |
| 2092 data()->AssignSpillRangeToLiveRange(first); | 2176 AssignSpillRangeToLiveRange(first); |
| 2093 } | 2177 } |
| 2094 range->MakeSpilled(); | 2178 range->MakeSpilled(); |
| 2095 } | 2179 } |
| 2096 | 2180 |
| 2097 | 2181 |
| 2098 OperandAssigner::OperandAssigner(RegisterAllocationData* data) : data_(data) {} | 2182 OperandAssigner::OperandAssigner(RegisterAllocationData* data) : data_(data) {} |
| 2099 | 2183 |
| 2100 | 2184 |
| 2101 void OperandAssigner::AssignSpillSlots() { | 2185 void OperandAssigner::AssignSpillSlots() { |
| 2102 auto& spill_ranges = data()->spill_ranges(); | 2186 auto& spill_ranges = data()->spill_ranges(); |
| (...skipping 397 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2500 moves = it->first.first; | 2584 moves = it->first.first; |
| 2501 } | 2585 } |
| 2502 // Gather all MoveOperands for a single ParallelMove. | 2586 // Gather all MoveOperands for a single ParallelMove. |
| 2503 auto move = new (code_zone()) MoveOperands(it->first.second, it->second); | 2587 auto move = new (code_zone()) MoveOperands(it->first.second, it->second); |
| 2504 auto eliminate = moves->PrepareInsertAfter(move); | 2588 auto eliminate = moves->PrepareInsertAfter(move); |
| 2505 to_insert.push_back(move); | 2589 to_insert.push_back(move); |
| 2506 if (eliminate != nullptr) to_eliminate.push_back(eliminate); | 2590 if (eliminate != nullptr) to_eliminate.push_back(eliminate); |
| 2507 } | 2591 } |
| 2508 } | 2592 } |
| 2509 | 2593 |
| 2594 | |
| 2595 unsigned GreedyAllocator::GetLiveRangeSize(LiveRange* range) { | |
| 2596 auto interval = range->first_interval(); | |
| 2597 if (interval == nullptr) return 0; | |
| 2598 | |
| 2599 unsigned size = 0; | |
| 2600 while (interval != nullptr) { | |
| 2601 size += (interval->end().Value() - interval->start().Value()); | |
| 2602 interval = interval->next(); | |
| 2603 } | |
| 2604 | |
| 2605 DCHECK_NE(0, size); | |
| 2606 return size; | |
| 2607 } | |
| 2608 | |
| 2609 GreedyAllocator::GreedyAllocator(RegisterAllocationData* data, | |
| 2610 RegisterKind kind) | |
| 2611 : data_(data), | |
| 2612 mode_(kind), | |
| 2613 num_registers_(GetRegisterCount(data->config(), kind)), | |
| 2614 allocations_(data->allocation_zone()), | |
| 2615 queue_(data->allocation_zone()) {} | |
| 2616 | |
| 2617 | |
| 2618 void GreedyAllocator::AssignRangeToRegister(int reg_id, LiveRange* range) { | |
| 2619 allocations_[reg_id]->Insert(range); | |
| 2620 if (range->HasRegisterAssigned()) { | |
| 2621 DCHECK_EQ(reg_id, range->assigned_register()); | |
| 2622 return; | |
| 2623 } | |
| 2624 range->set_assigned_register(reg_id); | |
| 2625 } | |
| 2626 | |
| 2627 | |
| 2628 float GreedyAllocator::CalculateSpillWeight(LiveRange* range) { | |
| 2629 if (range->IsFixed()) return std::numeric_limits<float>::max(); | |
| 2630 | |
| 2631 if (range->FirstHint() != nullptr && range->FirstHint()->IsRegister()) { | |
| 2632 return std::numeric_limits<float>::max(); | |
| 2633 } | |
| 2634 | |
| 2635 unsigned use_count = 0; | |
| 2636 auto* pos = range->first_pos(); | |
| 2637 while (pos != nullptr) { | |
| 2638 use_count++; | |
| 2639 pos = pos->next(); | |
| 2640 } | |
| 2641 | |
| 2642 // GetLiveRangeSize is DCHECK-ed to not be 0 | |
|
jvoung (off chromium)
2015/04/21 23:34:06
GetLiveRangeSize can be 0 if range->first_interval
Mircea Trofin
2015/04/22 04:37:33
Thanks! Moved the check here.
| |
| 2643 return static_cast<float>(use_count) / | |
| 2644 static_cast<float>(GetLiveRangeSize(range)); | |
| 2645 } | |
| 2646 | |
| 2647 | |
| 2648 float GreedyAllocator::CalculateMaxSpillWeight( | |
| 2649 const ZoneSet<LiveRange*>& ranges) { | |
| 2650 float max = 0.0; | |
| 2651 for (auto* r : ranges) { | |
| 2652 max = std::max(max, CalculateSpillWeight(r)); | |
| 2653 } | |
| 2654 return max; | |
| 2655 } | |
| 2656 | |
| 2657 | |
| 2658 void GreedyAllocator::Evict(LiveRange* range) { | |
| 2659 bool removed = allocations_[range->assigned_register()]->Remove(range); | |
| 2660 DCHECK(removed); | |
| 2661 } | |
| 2662 | |
| 2663 | |
| 2664 bool GreedyAllocator::TryAllocatePhysicalRegister( | |
| 2665 unsigned reg_id, LiveRange* range, ZoneSet<LiveRange*>* conflicting) { | |
| 2666 auto* segment = range->first_interval(); | |
| 2667 | |
| 2668 auto* alloc_info = allocations_[reg_id]; | |
| 2669 while (segment != nullptr) { | |
| 2670 if (auto* existing = alloc_info->Find(segment)) { | |
| 2671 DCHECK(existing->HasRegisterAssigned()); | |
| 2672 conflicting->insert(existing); | |
| 2673 } | |
| 2674 segment = segment->next(); | |
| 2675 } | |
| 2676 if (!conflicting->empty()) return false; | |
| 2677 // No conflicts means we can safely allocate this register to this range. | |
| 2678 AssignRangeToRegister(reg_id, range); | |
| 2679 return true; | |
| 2680 } | |
| 2681 | |
| 2682 bool GreedyAllocator::TryAllocate(LiveRange* current, | |
| 2683 ZoneSet<LiveRange*>* conflicting) { | |
| 2684 if (current->HasSpillOperand()) { | |
| 2685 data()->Spill(current); | |
| 2686 return true; | |
| 2687 } | |
| 2688 if (current->IsFixed()) { | |
| 2689 return TryAllocatePhysicalRegister(current->assigned_register(), current, | |
| 2690 conflicting); | |
| 2691 } | |
| 2692 | |
| 2693 if (current->HasRegisterAssigned()) { | |
| 2694 int reg_id = current->assigned_register(); | |
| 2695 return TryAllocatePhysicalRegister(reg_id, current, conflicting); | |
| 2696 } | |
| 2697 | |
| 2698 for (unsigned candidate_reg = 0; candidate_reg < allocations_.size(); | |
| 2699 candidate_reg++) { | |
| 2700 if (TryAllocatePhysicalRegister(candidate_reg, current, conflicting)) { | |
| 2701 conflicting->clear(); | |
| 2702 return true; | |
| 2703 } | |
| 2704 } | |
| 2705 return false; | |
| 2706 } | |
| 2707 | |
| 2708 LiveRange* GreedyAllocator::SpillBetweenUntil(LiveRange* range, | |
| 2709 LifetimePosition start, | |
| 2710 LifetimePosition until, | |
| 2711 LifetimePosition end) { | |
| 2712 CHECK(start.Value() < end.Value()); | |
| 2713 auto second_part = data()->SplitRangeAt(range, start); | |
| 2714 | |
| 2715 if (second_part->Start().Value() < end.Value()) { | |
| 2716 // The split result intersects with [start, end[. | |
| 2717 // Split it at position between ]start+1, end[, spill the middle part | |
| 2718 // and put the rest to unhandled. | |
| 2719 auto third_part_end = end.PrevStart().End(); | |
| 2720 if (data()->IsBlockBoundary(end.Start())) { | |
| 2721 third_part_end = end.Start(); | |
| 2722 } | |
| 2723 auto third_part = data()->SplitBetween( | |
| 2724 second_part, Max(second_part->Start().End(), until), third_part_end); | |
| 2725 | |
| 2726 DCHECK(third_part != second_part); | |
| 2727 | |
| 2728 data()->Spill(second_part); | |
| 2729 return third_part; | |
| 2730 } else { | |
| 2731 // The split result does not intersect with [start, end[. | |
| 2732 // Nothing to spill. Just put it to unhandled as whole. | |
|
jvoung (off chromium)
2015/04/21 23:34:06
nit: This comment seems to come from the LinearSca
Mircea Trofin
2015/04/22 04:37:33
Most likely this copy of the API will be deleted o
| |
| 2733 return second_part; | |
| 2734 } | |
| 2735 } | |
| 2736 | |
| 2737 | |
| 2738 void GreedyAllocator::Enqueue(LiveRange* range) { | |
| 2739 if (range == nullptr || range->IsEmpty()) return; | |
| 2740 unsigned size = GetLiveRangeSize(range); | |
| 2741 queue_.push(std::make_pair(size, range)); | |
| 2742 } | |
| 2743 | |
| 2744 | |
| 2745 // TODO(mtrofin): consolidate with identical code segment in | |
| 2746 // LinearScanAllocator::AllocateRegisters | |
| 2747 bool GreedyAllocator::HandleSpillOperands(LiveRange* range) { | |
| 2748 auto position = range->Start(); | |
| 2749 TRACE("Processing interval %d start=%d\n", range->id(), position.Value()); | |
| 2750 | |
| 2751 if (!range->HasNoSpillType()) { | |
| 2752 TRACE("Live range %d already has a spill operand\n", range->id()); | |
| 2753 auto next_pos = position; | |
| 2754 if (next_pos.IsGapPosition()) { | |
| 2755 next_pos = next_pos.NextStart(); | |
| 2756 } | |
| 2757 auto pos = range->NextUsePositionRegisterIsBeneficial(next_pos); | |
| 2758 // If the range already has a spill operand and it doesn't need a | |
| 2759 // register immediately, split it and spill the first part of the range. | |
| 2760 if (pos == nullptr) { | |
| 2761 data()->Spill(range); | |
| 2762 return true; | |
| 2763 } else if (pos->pos().Value() > range->Start().NextStart().Value()) { | |
| 2764 // Do not spill live range eagerly if use position that can benefit from | |
| 2765 // the register is too close to the start of live range. | |
| 2766 auto* reminder = SpillBetweenUntil(range, position, position, pos->pos()); | |
| 2767 Enqueue(reminder); | |
| 2768 return true; | |
| 2769 } | |
| 2770 } | |
| 2771 return false; | |
| 2772 // TODO(mtrofin): Do we need this? | |
| 2773 // return (TryReuseSpillForPhi(range)); | |
| 2774 } | |
| 2775 | |
| 2776 | |
| 2777 void GreedyAllocator::AllocateRegisters() { | |
| 2778 for (auto range : data()->live_ranges()) { | |
| 2779 if (range == nullptr) continue; | |
| 2780 if (range->Kind() == mode_) { | |
| 2781 DCHECK(!range->HasRegisterAssigned() && !range->IsSpilled()); | |
| 2782 TRACE("Enqueueing live range %d to priority queue \n", range->id()); | |
| 2783 Enqueue(range); | |
| 2784 } | |
| 2785 } | |
| 2786 | |
| 2787 allocations_.resize(num_registers_); | |
| 2788 auto* zone = data()->allocation_zone(); | |
| 2789 for (int i = 0; i < num_registers_; i++) { | |
| 2790 allocations_[i] = new (zone) CoallescedLiveRanges(zone); | |
| 2791 } | |
| 2792 | |
| 2793 for (auto* current : GetFixedRegisters(data(), mode_)) { | |
| 2794 if (current != nullptr) { | |
| 2795 DCHECK_EQ(mode_, current->Kind()); | |
| 2796 int reg_nr = current->assigned_register(); | |
| 2797 bool inserted = allocations_[reg_nr]->Insert(current); | |
| 2798 CHECK(inserted); | |
| 2799 } | |
| 2800 } | |
| 2801 | |
| 2802 while (!queue_.empty()) { | |
| 2803 auto current_pair = queue_.top(); | |
| 2804 queue_.pop(); | |
| 2805 auto current = current_pair.second; | |
| 2806 if (HandleSpillOperands(current)) continue; | |
| 2807 ZoneSet<LiveRange*> conflicting(zone); | |
| 2808 if (!TryAllocate(current, &conflicting)) { | |
| 2809 DCHECK(conflicting.size()); | |
|
jvoung (off chromium)
2015/04/21 23:34:06
Is there an !empty() method to check instead of si
Mircea Trofin
2015/04/22 04:37:33
Done.
| |
| 2810 float this_max = CalculateSpillWeight(current); | |
| 2811 float max_conflicting = CalculateMaxSpillWeight(conflicting); | |
| 2812 if (max_conflicting < this_max) { | |
| 2813 for (auto* conflict : conflicting) { | |
| 2814 Evict(conflict); | |
| 2815 Enqueue(conflict); | |
| 2816 } | |
| 2817 conflicting.clear(); | |
| 2818 bool allocated = TryAllocate(current, &conflicting); | |
| 2819 CHECK(allocated); | |
| 2820 DCHECK_EQ(0, conflicting.size()); | |
|
jvoung (off chromium)
2015/04/21 23:34:06
conflicting.empty() ?
Mircea Trofin
2015/04/22 04:37:33
Done.
| |
| 2821 } else { | |
| 2822 DCHECK(!current->IsFixed() || current->CanBeSpilled(current->Start())); | |
| 2823 bool allocated = AllocateBlockedRange(current, conflicting); | |
| 2824 CHECK(allocated); | |
| 2825 } | |
| 2826 } | |
| 2827 } | |
| 2828 | |
| 2829 BitVector* assigned_registers = new (zone) BitVector(num_registers_, zone); | |
| 2830 for (size_t i = 0; i < allocations_.size(); ++i) { | |
| 2831 if (!allocations_[i]->IsEmpty()) { | |
| 2832 assigned_registers->Add(i); | |
| 2833 } | |
| 2834 } | |
| 2835 data()->frame()->SetAllocatedRegisters(assigned_registers); | |
| 2836 } | |
| 2837 | |
| 2838 | |
| 2839 bool GreedyAllocator::AllocateBlockedRange( | |
| 2840 LiveRange* current, const ZoneSet<LiveRange*>& conflicts) { | |
| 2841 auto register_use = current->NextRegisterPosition(current->Start()); | |
| 2842 if (register_use == nullptr) { | |
| 2843 // There is no use in the current live range that requires a register. | |
| 2844 // We can just spill it. | |
| 2845 data()->Spill(current); | |
| 2846 return true; | |
| 2847 } | |
| 2848 | |
| 2849 auto second_part = data()->SplitRangeAt(current, register_use->pos()); | |
| 2850 data()->Spill(second_part); | |
| 2851 | |
| 2852 return true; | |
| 2853 } | |
| 2854 | |
| 2855 | |
| 2510 } // namespace compiler | 2856 } // namespace compiler |
| 2511 } // namespace internal | 2857 } // namespace internal |
| 2512 } // namespace v8 | 2858 } // namespace v8 |
| OLD | NEW |