|
137 | 137 | #include "PhiStorageOptimizer.h"
|
138 | 138 | #include "swift/AST/Decl.h"
|
139 | 139 | #include "swift/Basic/BlotSetVector.h"
|
| 140 | +#include "swift/Basic/FrozenMultiMap.h" |
140 | 141 | #include "swift/Basic/Range.h"
|
141 | 142 | #include "swift/SIL/BasicBlockUtils.h"
|
142 | 143 | #include "swift/SIL/DebugUtils.h"
|
@@ -522,6 +523,9 @@ struct AddressLoweringState {
|
522 | 523 | // Handle moves from a phi's operand storage to the phi storage.
|
523 | 524 | std::unique_ptr<PhiRewriter> phiRewriter;
|
524 | 525 |
|
| 526 | + // Projections created for uses, recorded in order to be sunk. |
| 527 | + SmallVector<SILValue, 16> useProjections; |
| 528 | + |
525 | 529 | AddressLoweringState(SILFunction *function, DominanceInfo *domInfo,
|
526 | 530 | DeadEndBlocks *deBlocks)
|
527 | 531 | : function(function), loweredFnConv(getLoweredFnConv(function)),
|
@@ -1147,6 +1151,8 @@ class OpaqueStorageAllocation {
|
1147 | 1151 | /// jointly postdominate.
|
1148 | 1152 | void finalizeOpaqueStorage();
|
1149 | 1153 |
|
| 1154 | + void sinkProjections(); |
| 1155 | + |
1150 | 1156 | protected:
|
1151 | 1157 | void allocateValue(SILValue value);
|
1152 | 1158 | bool findProjectionIntoUseImpl(SILValue value,
|
@@ -1501,6 +1507,54 @@ AllocStackInst *OpaqueStorageAllocation::createStackAllocation(SILValue value) {
|
1501 | 1507 | return alloc;
|
1502 | 1508 | }
|
1503 | 1509 |
|
| 1510 | +namespace { |
| 1511 | +enum class SinkResult { |
| 1512 | + NoUsers, |
| 1513 | + Unmoved, |
| 1514 | + Moved, |
| 1515 | +}; |
| 1516 | +SinkResult sinkToUses(SingleValueInstruction *svi, DominanceInfo *domInfo) { |
| 1517 | + // Fast paths for 0 and 1 users. |
| 1518 | + |
| 1519 | + if (svi->use_begin() == svi->use_end()) { |
| 1520 | + return SinkResult::NoUsers; |
| 1521 | + } |
| 1522 | + |
| 1523 | + if (auto *use = svi->getSingleUse()) { |
| 1524 | + auto *user = use->getUser(); |
| 1525 | + if (user == svi->getNextInstruction()) |
| 1526 | + return SinkResult::Unmoved; |
| 1527 | + svi->moveBefore(user); |
| 1528 | + return SinkResult::Moved; |
| 1529 | + } |
| 1530 | + |
| 1531 | + // Compute the lca and sink the instruction to before its first user or its |
| 1532 | + // end if there are none. |
| 1533 | + |
| 1534 | + SILBasicBlock *lca = domInfo->getLeastCommonAncestorOfUses(svi); |
| 1535 | + |
| 1536 | + // The lca may contain a user. Look for the user to insert before it. |
| 1537 | + |
| 1538 | + InstructionSet userSet(svi->getFunction()); |
| 1539 | + for (auto user : svi->getUsers()) { |
| 1540 | + userSet.insert(user); |
| 1541 | + } |
| 1542 | + |
| 1543 | + for (auto &instruction : *lca) { |
| 1544 | + if (userSet.contains(&instruction)) { |
| 1545 | + if (&instruction == svi->getNextInstruction()) |
| 1546 | + return SinkResult::Unmoved; |
| 1547 | + svi->moveBefore(&instruction); |
| 1548 | + return SinkResult::Moved; |
| 1549 | + } |
| 1550 | + } |
| 1551 | + |
| 1552 | + // No user was found in the lca, move to before the end. |
| 1553 | + svi->moveBefore(&lca->back()); |
| 1554 | + return SinkResult::Moved; |
| 1555 | +} |
| 1556 | +} // end anonymous namespace |
| 1557 | + |
1504 | 1558 | void OpaqueStorageAllocation::finalizeOpaqueStorage() {
|
1505 | 1559 | SmallVector<SILBasicBlock *, 4> boundary;
|
1506 | 1560 | for (auto maybeAlloc : allocs) {
|
@@ -1531,6 +1585,79 @@ void OpaqueStorageAllocation::finalizeOpaqueStorage() {
|
1531 | 1585 | }
|
1532 | 1586 | }
|
1533 | 1587 |
|
| 1588 | +void OpaqueStorageAllocation::sinkProjections() { |
| 1589 | + // First, sink use projections to their uses. |
| 1590 | + // |
| 1591 | + // Done in reverse order because outer projections are materialized first and |
| 1592 | + // so appear in `useProjections` before inner projections, and inner |
| 1593 | + // projections must be sunk first. |
| 1594 | + for (auto projection : llvm::reverse(pass.useProjections)) { |
| 1595 | + assert(projection); |
| 1596 | + auto *svi = dyn_cast<SingleValueInstruction>(projection); |
| 1597 | + assert(svi); |
| 1598 | + auto sank = sinkToUses(svi, pass.domInfo); |
| 1599 | + if (sank == SinkResult::NoUsers) { |
| 1600 | + pass.deleter.forceDelete(svi); |
| 1601 | + } |
| 1602 | + } |
| 1603 | + |
| 1604 | + // Second, sink all storage from the valueStorageMap. |
| 1605 | + // |
| 1606 | + // Calculate the (multi-rooted) tree of projections represented by their |
| 1607 | + // indices in `valueStorageMap` (a). Then visit the projections in |
| 1608 | + // post-order (b), in order to visit all inner projections before visiting |
| 1609 | + // outer projections. |
| 1610 | + |
| 1611 | + // (a) Assemble the tree. |
| 1612 | + SmallFrozenMultiMap<uint32_t, uint32_t, 32> projections; |
| 1613 | + SmallVector<uint32_t, 16> roots; |
| 1614 | + uint32_t index = 0; |
| 1615 | + for (auto &valueAndStorage : pass.valueStorageMap) { |
| 1616 | + auto &storage = valueAndStorage.storage; |
| 1617 | + if (storage.isProjection()) { |
| 1618 | + projections.insert(storage.projectedStorageID, index); |
| 1619 | + } else { |
| 1620 | + roots.push_back(index); |
| 1621 | + } |
| 1622 | + ++index; |
| 1623 | + } |
| 1624 | + projections.setFrozen(); |
| 1625 | + |
| 1626 | + auto sinkProjectionAtIndex = [&](uint32_t index) { |
| 1627 | + auto pair = pass.valueStorageMap[index]; |
| 1628 | + auto addr = pair.storage.getMaterializedAddress(); |
| 1629 | + if (!pair.storage.isProjection()) |
| 1630 | + return; |
| 1631 | + auto *inst = dyn_cast<SingleValueInstruction>(addr); |
| 1632 | + if (!inst) |
| 1633 | + return; |
| 1634 | + auto sank = sinkToUses(inst, pass.domInfo); |
| 1635 | + if (sank == SinkResult::NoUsers) { |
| 1636 | + pass.deleter.forceDelete(inst); |
| 1637 | + } |
| 1638 | + }; |
| 1639 | + |
| 1640 | + // (b) Visit the projections in post-order. |
| 1641 | + for (auto root : roots) { |
| 1642 | + SmallVector<std::pair<uint32_t, size_t>, 32> stack; |
| 1643 | + stack.push_back({root, 0}); |
| 1644 | + while (!stack.empty()) { |
| 1645 | + while (stack.back().second < |
| 1646 | + projections.find(stack.back().first) |
| 1647 | + .transform([](auto &i) { return i.size(); }) |
| 1648 | + .value_or(0)) { |
| 1649 | + auto child = |
| 1650 | + projections.find(stack.back().first).value()[stack.back().second]; |
| 1651 | + stack.back().second++; |
| 1652 | + stack.push_back({child, 0}); |
| 1653 | + } |
| 1654 | + auto index = stack.back().first; |
| 1655 | + sinkProjectionAtIndex(index); |
| 1656 | + stack.pop_back(); |
| 1657 | + } |
| 1658 | + } |
| 1659 | +} |
| 1660 | + |
1534 | 1661 | //===----------------------------------------------------------------------===//
|
1535 | 1662 | // AddressMaterialization
|
1536 | 1663 | //
|
@@ -1590,6 +1717,8 @@ class AddressMaterialization {
|
1590 | 1717 | SILValue elementValue, unsigned fieldIdx);
|
1591 | 1718 |
|
1592 | 1719 | SILValue materializeProjectionIntoUse(Operand *operand, bool intoPhiOperand);
|
| 1720 | + SILValue materializeProjectionIntoUseImpl(Operand *operand, |
| 1721 | + bool intoPhiOperand); |
1593 | 1722 |
|
1594 | 1723 | SILValue materializeComposingUser(SingleValueInstruction *user,
|
1595 | 1724 | bool intoPhiOperand) {
|
@@ -1771,12 +1900,20 @@ SILValue AddressMaterialization::materializeTupleExtract(
|
1771 | 1900 | elementValue->getType().getAddressType());
|
1772 | 1901 | }
|
1773 | 1902 |
|
| 1903 | +SILValue |
| 1904 | +AddressMaterialization::materializeProjectionIntoUse(Operand *operand, |
| 1905 | + bool intoPhiOperand) { |
| 1906 | + auto projection = materializeProjectionIntoUseImpl(operand, intoPhiOperand); |
| 1907 | + pass.useProjections.push_back(projection); |
| 1908 | + return projection; |
| 1909 | +} |
| 1910 | + |
1774 | 1911 | /// Recursively materialize the address of a subobject that is a member of the
|
1775 | 1912 | /// operand's user. The operand's user must be an aggregate struct, tuple, enum,
|
1776 | 1913 | /// init_existential_value.
|
1777 | 1914 | SILValue
|
1778 |
| -AddressMaterialization::materializeProjectionIntoUse(Operand *operand, |
1779 |
| - bool intoPhiOperand) { |
| 1915 | +AddressMaterialization::materializeProjectionIntoUseImpl(Operand *operand, |
| 1916 | + bool intoPhiOperand) { |
1780 | 1917 | SILInstruction *user = operand->getUser();
|
1781 | 1918 | switch (user->getKind()) {
|
1782 | 1919 | default:
|
@@ -4183,6 +4320,8 @@ void AddressLowering::runOnFunction(SILFunction *function) {
|
4183 | 4320 |
|
4184 | 4321 | allocator.finalizeOpaqueStorage();
|
4185 | 4322 |
|
| 4323 | + allocator.sinkProjections(); |
| 4324 | + |
4186 | 4325 | deleteRewrittenInstructions(pass);
|
4187 | 4326 |
|
4188 | 4327 | StackNesting::fixNesting(function);
|
|
0 commit comments