29
29
using namespace swift ;
30
30
31
31
STATISTIC (NumLoadPromoted, " Number of loads promoted" );
32
+ STATISTIC (NumLoadTakePromoted, " Number of load takes promoted" );
32
33
STATISTIC (NumDestroyAddrPromoted, " Number of destroy_addrs promoted" );
33
34
STATISTIC (NumAllocRemoved, " Number of allocations completely removed" );
34
35
@@ -1320,14 +1321,18 @@ class AllocOptimize {
1320
1321
DataflowContext(TheMemory, NumMemorySubElements, uses) {}
1321
1322
1322
1323
bool optimizeMemoryAccesses ();
1324
+
1325
+ // / If the allocation is an autogenerated allocation that is only stored to
1326
+ // / (after load promotion) then remove it completely.
1323
1327
bool tryToRemoveDeadAllocation ();
1324
1328
1325
1329
private:
1326
- bool promoteLoad (SILInstruction *Inst);
1330
+ bool promoteLoadCopy (SILInstruction *Inst);
1331
+ void promoteLoadTake (LoadInst *Inst, MutableArrayRef<AvailableValue> values);
1327
1332
void promoteDestroyAddr (DestroyAddrInst *dai,
1328
1333
MutableArrayRef<AvailableValue> values);
1329
- bool canPromoteDestroyAddr (DestroyAddrInst *dai ,
1330
- SmallVectorImpl<AvailableValue> &availableValues);
1334
+ bool canPromoteTake (SILInstruction *i ,
1335
+ SmallVectorImpl<AvailableValue> &availableValues);
1331
1336
};
1332
1337
1333
1338
} // end anonymous namespace
@@ -1361,7 +1366,7 @@ static SILValue tryFindSrcAddrForLoad(SILInstruction *i) {
1361
1366
// / cross element accesses have been scalarized.
1362
1367
// /
1363
1368
// / This returns true if the load has been removed from the program.
1364
- bool AllocOptimize::promoteLoad (SILInstruction *Inst) {
1369
+ bool AllocOptimize::promoteLoadCopy (SILInstruction *Inst) {
1365
1370
// Note that we intentionally don't support forwarding of weak pointers,
1366
1371
// because the underlying value may drop be deallocated at any time. We would
1367
1372
// have to prove that something in this function is holding the weak value
@@ -1464,19 +1469,19 @@ bool AllocOptimize::promoteLoad(SILInstruction *Inst) {
1464
1469
}
1465
1470
1466
1471
// / Return true if we can promote the given destroy.
1467
- bool AllocOptimize::canPromoteDestroyAddr (
1468
- DestroyAddrInst *dai , SmallVectorImpl<AvailableValue> &availableValues) {
1469
- SILValue address = dai ->getOperand ();
1472
+ bool AllocOptimize::canPromoteTake (
1473
+ SILInstruction *inst , SmallVectorImpl<AvailableValue> &availableValues) {
1474
+ SILValue address = inst ->getOperand (0 );
1470
1475
1471
1476
// We cannot promote destroys of address-only types, because we can't expose
1472
1477
// the load.
1473
1478
SILType loadTy = address->getType ().getObjectType ();
1474
- if (loadTy.isAddressOnly (*dai ->getFunction ()))
1479
+ if (loadTy.isAddressOnly (*inst ->getFunction ()))
1475
1480
return false ;
1476
1481
1477
1482
// If the box has escaped at this instruction, we can't safely promote the
1478
1483
// load.
1479
- if (DataflowContext.hasEscapedAt (dai ))
1484
+ if (DataflowContext.hasEscapedAt (inst ))
1480
1485
return false ;
1481
1486
1482
1487
// Compute the access path down to the field so we can determine precise
@@ -1498,15 +1503,15 @@ bool AllocOptimize::canPromoteDestroyAddr(
1498
1503
// return false. We have nothing further to do.
1499
1504
SmallVector<AvailableValue, 8 > tmpList;
1500
1505
tmpList.resize (NumMemorySubElements);
1501
- if (!DataflowContext.computeAvailableValues (dai, firstElt, numLoadSubElements,
1502
- requiredElts, tmpList))
1506
+ if (!DataflowContext.computeAvailableValues (
1507
+ inst, firstElt, numLoadSubElements, requiredElts, tmpList))
1503
1508
return false ;
1504
1509
1505
1510
// Now check that we can perform a take upon our available values. This
1506
1511
// implies today that our value is fully available. If the value is not fully
1507
1512
// available, we would need to split stores to promote this destroy_addr. We
1508
1513
// do not support that yet.
1509
- AvailableValueAggregator agg (dai , tmpList, Uses, deadEndBlocks,
1514
+ AvailableValueAggregator agg (inst , tmpList, Uses, deadEndBlocks,
1510
1515
true /* isTake*/ );
1511
1516
if (!agg.canTake (loadTy, firstElt))
1512
1517
return false ;
@@ -1551,29 +1556,56 @@ void AllocOptimize::promoteDestroyAddr(
1551
1556
dai->eraseFromParent ();
1552
1557
}
1553
1558
1559
+ void AllocOptimize::promoteLoadTake (
1560
+ LoadInst *li, MutableArrayRef<AvailableValue> availableValues) {
1561
+ assert (li->getOwnershipQualifier () == LoadOwnershipQualifier::Take &&
1562
+ " load [copy], load [trivial], load should be handled by "
1563
+ " promoteLoadCopy" );
1564
+ SILValue address = li->getOperand ();
1565
+ SILType loadTy = address->getType ().getObjectType ();
1566
+
1567
+ // Compute the access path down to the field so we can determine precise
1568
+ // def/use behavior.
1569
+ unsigned firstElt = computeSubelement (address, TheMemory);
1570
+
1571
+ // Aggregate together all of the subelements into something that has the same
1572
+ // type as the load did, and emit smaller) loads for any subelements that were
1573
+ // not available.
1574
+ AvailableValueAggregator agg (li, availableValues, Uses, deadEndBlocks,
1575
+ true /* isTake*/ );
1576
+ SILValue newVal = agg.aggregateValues (loadTy, address, firstElt);
1577
+
1578
+ ++NumLoadTakePromoted;
1579
+
1580
+ LLVM_DEBUG (llvm::dbgs () << " *** Promoting load_take: " << *li << " \n " );
1581
+ LLVM_DEBUG (llvm::dbgs () << " To value: " << *newVal << " \n " );
1582
+
1583
+ // Then perform the RAUW.
1584
+ li->replaceAllUsesWith (newVal);
1585
+ li->eraseFromParent ();
1586
+ }
1587
+
1554
1588
namespace {
1555
1589
1556
- struct DestroyAddrPromotionState {
1557
- ArrayRef<SILInstruction *> destroys ;
1558
- SmallVector<unsigned , 8 > destroyAddrIndices ;
1590
+ struct TakePromotionState {
1591
+ ArrayRef<SILInstruction *> takeInsts ;
1592
+ SmallVector<unsigned , 8 > takeInstIndices ;
1559
1593
SmallVector<AvailableValue, 32 > availableValueList;
1560
1594
SmallVector<unsigned , 8 > availableValueStartOffsets;
1561
1595
1562
- DestroyAddrPromotionState (ArrayRef<SILInstruction *> destroys )
1563
- : destroys(destroys ) {}
1596
+ TakePromotionState (ArrayRef<SILInstruction *> takeInsts )
1597
+ : takeInsts(takeInsts ) {}
1564
1598
1565
- unsigned size () const {
1566
- return destroyAddrIndices.size ();
1567
- }
1599
+ unsigned size () const { return takeInstIndices.size (); }
1568
1600
1569
- void initializeForDestroyAddr (unsigned destroyAddrIndex ) {
1601
+ void initializeForTakeInst (unsigned takeInstIndex ) {
1570
1602
availableValueStartOffsets.push_back (availableValueList.size ());
1571
- destroyAddrIndices .push_back (destroyAddrIndex );
1603
+ takeInstIndices .push_back (takeInstIndex );
1572
1604
}
1573
1605
1574
- std::pair<DestroyAddrInst *, MutableArrayRef<AvailableValue>>
1606
+ std::pair<SILInstruction *, MutableArrayRef<AvailableValue>>
1575
1607
getData (unsigned index) {
1576
- unsigned destroyAddrIndex = destroyAddrIndices [index];
1608
+ unsigned takeInstIndex = takeInstIndices [index];
1577
1609
unsigned startOffset = availableValueStartOffsets[index];
1578
1610
unsigned count;
1579
1611
@@ -1585,36 +1617,21 @@ struct DestroyAddrPromotionState {
1585
1617
1586
1618
MutableArrayRef<AvailableValue> values (&availableValueList[startOffset],
1587
1619
count);
1588
- auto *dai = cast<DestroyAddrInst>(destroys[destroyAddrIndex]);
1589
- return {dai, values};
1620
+ return {takeInsts[takeInstIndex], values};
1590
1621
}
1591
1622
};
1592
1623
1593
1624
} // end anonymous namespace
1594
1625
1595
- // / If the allocation is an autogenerated allocation that is only stored to
1596
- // / (after load promotion) then remove it completely.
1597
- bool AllocOptimize::tryToRemoveDeadAllocation () {
1598
- assert ((isa<AllocBoxInst>(TheMemory) || isa<AllocStackInst>(TheMemory)) &&
1599
- " Unhandled allocation case" );
1600
-
1601
- auto *f = TheMemory->getFunction ();
1602
-
1603
- // We don't want to remove allocations that are required for useful debug
1604
- // information at -O0. As such, we only remove allocations if:
1605
- //
1606
- // 1. They are in a transparent function.
1607
- // 2. They are in a normal function, but didn't come from a VarDecl, or came
1608
- // from one that was autogenerated or inlined from a transparent function.
1609
- SILLocation loc = TheMemory->getLoc ();
1610
- if (!f->isTransparent () &&
1611
- loc.getAsASTNode <VarDecl>() && !loc.isAutoGenerated () &&
1612
- !loc.is <MandatoryInlinedLocation>())
1613
- return false ;
1614
-
1615
- // Check the uses list to see if there are any non-store uses left over after
1616
- // load promotion and other things PMO does.
1617
- for (auto &u : Uses) {
1626
+ // Check if our use list has any non store, non take uses that keep the value
1627
+ // alive. Returns nullptr on success and the user that prevents removal on
1628
+ // failure.
1629
+ //
1630
+ // NOTE: This also gathers up any takes that we need to process.
1631
+ static SILInstruction *
1632
+ checkForNonStoreNonTakeUses (ArrayRef<PMOMemoryUse> uses,
1633
+ SmallVectorImpl<SILInstruction *> &loadTakeList) {
1634
+ for (auto &u : uses) {
1618
1635
// Ignore removed instructions.
1619
1636
if (u.Inst == nullptr )
1620
1637
continue ;
@@ -1623,33 +1640,73 @@ bool AllocOptimize::tryToRemoveDeadAllocation() {
1623
1640
case PMOUseKind::Assign:
1624
1641
// Until we can promote the value being destroyed by the assign, we can
1625
1642
// not remove deallocations with such assigns.
1626
- return false ;
1643
+ return u. Inst ;
1627
1644
case PMOUseKind::InitOrAssign:
1628
- break ; // These don't prevent removal.
1645
+ continue ; // These don't prevent removal.
1646
+ case PMOUseKind::Load:
1647
+ // For now only handle takes from alloc_stack.
1648
+ //
1649
+ // TODO: It should be implementable, but it has not been needed yet.
1650
+ if (auto *li = dyn_cast<LoadInst>(u.Inst )) {
1651
+ if (li->getOwnershipQualifier () == LoadOwnershipQualifier::Take) {
1652
+ loadTakeList.push_back (li);
1653
+ continue ;
1654
+ }
1655
+ }
1656
+ return u.Inst ;
1629
1657
case PMOUseKind::Initialization:
1630
1658
if (!isa<ApplyInst>(u.Inst ) &&
1631
1659
// A copy_addr that is not a take affects the retain count
1632
1660
// of the source.
1633
1661
(!isa<CopyAddrInst>(u.Inst ) ||
1634
1662
cast<CopyAddrInst>(u.Inst )->isTakeOfSrc ()))
1635
- break ;
1663
+ continue ;
1636
1664
// FALL THROUGH.
1637
- LLVM_FALLTHROUGH;
1638
- case PMOUseKind::Load:
1665
+ LLVM_FALLTHROUGH;
1639
1666
case PMOUseKind::IndirectIn:
1640
1667
case PMOUseKind::InOutUse:
1641
1668
case PMOUseKind::Escape:
1642
- LLVM_DEBUG (llvm::dbgs () << " *** Failed to remove autogenerated alloc: "
1643
- " kept alive by: "
1644
- << *u.Inst );
1645
- return false ; // These do prevent removal.
1669
+ return u.Inst ; // These do prevent removal.
1646
1670
}
1647
1671
}
1648
1672
1673
+ return nullptr ;
1674
+ }
1675
+
1676
+ // We don't want to remove allocations that are required for useful debug
1677
+ // information at -O0. As such, we only remove allocations if:
1678
+ //
1679
+ // 1. They are in a transparent function.
1680
+ // 2. They are in a normal function, but didn't come from a VarDecl, or came
1681
+ // from one that was autogenerated or inlined from a transparent function.
1682
+ static bool isRemovableAutogeneratedAllocation (AllocationInst *TheMemory) {
1683
+ SILLocation loc = TheMemory->getLoc ();
1684
+ return TheMemory->getFunction ()->isTransparent () ||
1685
+ !loc.getAsASTNode <VarDecl>() || loc.isAutoGenerated () ||
1686
+ loc.is <MandatoryInlinedLocation>();
1687
+ }
1688
+
1689
+ bool AllocOptimize::tryToRemoveDeadAllocation () {
1690
+ assert ((isa<AllocBoxInst>(TheMemory) || isa<AllocStackInst>(TheMemory)) &&
1691
+ " Unhandled allocation case" );
1692
+
1693
+ if (!isRemovableAutogeneratedAllocation (TheMemory))
1694
+ return false ;
1695
+
1696
+ SmallVector<SILInstruction *, 8 > loadTakeList;
1697
+ // Check the uses list to see if there are any non-store uses left over after
1698
+ // load promotion and other things PMO does.
1699
+ if (auto *badUser = checkForNonStoreNonTakeUses (Uses, loadTakeList)) {
1700
+ LLVM_DEBUG (llvm::dbgs () << " *** Failed to remove autogenerated alloc: "
1701
+ " kept alive by: "
1702
+ << *badUser);
1703
+ return false ;
1704
+ }
1705
+
1649
1706
// If our memory is trivially typed, we can just remove it without needing to
1650
1707
// consider if the stored value needs to be destroyed. So at this point,
1651
1708
// delete the memory!
1652
- if (MemoryType.isTrivial (*f )) {
1709
+ if (MemoryType.isTrivial (*TheMemory-> getFunction () )) {
1653
1710
LLVM_DEBUG (llvm::dbgs () << " *** Removing autogenerated trivial allocation: "
1654
1711
<< *TheMemory);
1655
1712
@@ -1661,23 +1718,31 @@ bool AllocOptimize::tryToRemoveDeadAllocation() {
1661
1718
return true ;
1662
1719
}
1663
1720
1721
+ // Now make sure we can promote all load [take] and prepare state for each of
1722
+ // them.
1723
+ TakePromotionState loadTakeState (loadTakeList);
1724
+ for (auto p : llvm::enumerate (loadTakeList)) {
1725
+ loadTakeState.initializeForTakeInst (p.index ());
1726
+ if (!canPromoteTake (p.value (), loadTakeState.availableValueList ))
1727
+ return false ;
1728
+ }
1729
+
1664
1730
// Otherwise removing the deallocation will drop any releases. Check that
1665
1731
// there is nothing preventing removal.
1666
- DestroyAddrPromotionState state (Releases);
1667
-
1732
+ TakePromotionState destroyAddrState (Releases);
1668
1733
for (auto p : llvm::enumerate (Releases)) {
1669
1734
auto *r = p.value ();
1670
1735
if (r == nullptr )
1671
1736
continue ;
1672
1737
1673
1738
// We stash all of the destroy_addr that we see.
1674
1739
if (auto *dai = dyn_cast<DestroyAddrInst>(r)) {
1675
- state. initializeForDestroyAddr (p.index () /* destroyAddrIndex*/ );
1740
+ destroyAddrState. initializeForTakeInst (p.index () /* destroyAddrIndex*/ );
1676
1741
// Make sure we can actually promote this destroy addr. If we can not,
1677
1742
// then we must bail. In order to not gather available values twice, we
1678
1743
// gather the available values here that we will use to promote the
1679
1744
// values.
1680
- if (!canPromoteDestroyAddr (dai, state .availableValueList ))
1745
+ if (!canPromoteTake (dai, destroyAddrState .availableValueList ))
1681
1746
return false ;
1682
1747
continue ;
1683
1748
}
@@ -1689,14 +1754,22 @@ bool AllocOptimize::tryToRemoveDeadAllocation() {
1689
1754
return false ;
1690
1755
}
1691
1756
1692
- // If we reached this point, we can promote all of our destroy_addr.
1693
- for (unsigned i : range (state.size ())) {
1694
- DestroyAddrInst *dai;
1757
+ // If we reached this point, we can promote all of our destroy_addr and load
1758
+ // take. Since our load [take] may be available values for our destroy_addr,
1759
+ // we promote the destroy_addr first.
1760
+ for (unsigned i : range (destroyAddrState.size ())) {
1761
+ SILInstruction *dai;
1695
1762
MutableArrayRef<AvailableValue> values;
1696
- std::tie (dai, values) = state .getData (i);
1697
- promoteDestroyAddr (dai, values);
1763
+ std::tie (dai, values) = destroyAddrState .getData (i);
1764
+ promoteDestroyAddr (cast<DestroyAddrInst>( dai) , values);
1698
1765
// We do not need to unset releases, since we are going to exit here.
1699
1766
}
1767
+ for (unsigned i : range (loadTakeState.size ())) {
1768
+ SILInstruction *li;
1769
+ MutableArrayRef<AvailableValue> values;
1770
+ std::tie (li, values) = loadTakeState.getData (i);
1771
+ promoteLoadTake (cast<LoadInst>(li), values);
1772
+ }
1700
1773
1701
1774
LLVM_DEBUG (llvm::dbgs () << " *** Removing autogenerated non-trivial alloc: "
1702
1775
<< *TheMemory);
@@ -1719,7 +1792,7 @@ bool AllocOptimize::optimizeMemoryAccesses() {
1719
1792
auto &use = Uses[i];
1720
1793
// Ignore entries for instructions that got expanded along the way.
1721
1794
if (use.Inst && use.Kind == PMOUseKind::Load) {
1722
- if (promoteLoad (use.Inst )) {
1795
+ if (promoteLoadCopy (use.Inst )) {
1723
1796
Uses[i].Inst = nullptr ; // remove entry if load got deleted.
1724
1797
changed = true ;
1725
1798
}
0 commit comments