@@ -1489,7 +1489,12 @@ class AllocOptimize {
1489
1489
bool tryToRemoveDeadAllocation ();
1490
1490
1491
1491
private:
1492
- bool promoteLoadCopy (SILInstruction *Inst);
1492
+ Optional<std::pair<SILType, unsigned >>
1493
+ computeAvailableValues (SILValue SrcAddr, SILInstruction *Inst,
1494
+ SmallVectorImpl<AvailableValue> &AvailableValues);
1495
+ bool promoteLoadCopy (LoadInst *Inst);
1496
+ bool promoteLoadBorrow (LoadBorrowInst *Inst);
1497
+ bool promoteCopyAddr (CopyAddrInst *CAI);
1493
1498
void promoteLoadTake (LoadInst *Inst, MutableArrayRef<AvailableValue> values);
1494
1499
void promoteDestroyAddr (DestroyAddrInst *dai,
1495
1500
MutableArrayRef<AvailableValue> values);
@@ -1499,6 +1504,43 @@ class AllocOptimize {
1499
1504
1500
1505
} // end anonymous namespace
1501
1506
1507
+ Optional<std::pair<SILType, unsigned >> AllocOptimize::computeAvailableValues (
1508
+ SILValue SrcAddr, SILInstruction *Inst,
1509
+ SmallVectorImpl<AvailableValue> &AvailableValues) {
1510
+ // If the box has escaped at this instruction, we can't safely promote the
1511
+ // load.
1512
+ if (DataflowContext.hasEscapedAt (Inst))
1513
+ return None;
1514
+
1515
+ SILType LoadTy = SrcAddr->getType ().getObjectType ();
1516
+
1517
+ // If this is a load/copy_addr from a struct field that we want to promote,
1518
+ // compute the access path down to the field so we can determine precise
1519
+ // def/use behavior.
1520
+ unsigned FirstElt = computeSubelement (SrcAddr, TheMemory);
1521
+
1522
+ // If this is a load from within an enum projection, we can't promote it since
1523
+ // we don't track subelements in a type that could be changing.
1524
+ if (FirstElt == ~0U )
1525
+ return None;
1526
+
1527
+ unsigned NumLoadSubElements = getNumSubElements (LoadTy, Module);
1528
+
1529
+ // Set up the bitvector of elements being demanded by the load.
1530
+ SmallBitVector RequiredElts (NumMemorySubElements);
1531
+ RequiredElts.set (FirstElt, FirstElt + NumLoadSubElements);
1532
+
1533
+ AvailableValues.resize (NumMemorySubElements);
1534
+
1535
+ // Find out if we have any available values. If no bits are demanded, we
1536
+ // trivially succeed. This can happen when there is a load of an empty struct.
1537
+ if (NumLoadSubElements != 0 &&
1538
+ !DataflowContext.computeAvailableValues (
1539
+ Inst, FirstElt, NumLoadSubElements, RequiredElts, AvailableValues))
1540
+ return None;
1541
+
1542
+ return std::make_pair (LoadTy, FirstElt);
1543
+ }
1502
1544
1503
1545
// / If we are able to optimize \p Inst, return the source address that
1504
1546
// / instruction is loading from. If we can not optimize \p Inst, then just
@@ -1528,7 +1570,7 @@ static SILValue tryFindSrcAddrForLoad(SILInstruction *i) {
1528
1570
// / cross element accesses have been scalarized.
1529
1571
// /
1530
1572
// / This returns true if the load has been removed from the program.
1531
- bool AllocOptimize::promoteLoadCopy (SILInstruction *Inst) {
1573
+ bool AllocOptimize::promoteLoadCopy (LoadInst *Inst) {
1532
1574
// Note that we intentionally don't support forwarding of weak pointers,
1533
1575
// because the underlying value may drop be deallocated at any time. We would
1534
1576
// have to prove that something in this function is holding the weak value
@@ -1541,52 +1583,104 @@ bool AllocOptimize::promoteLoadCopy(SILInstruction *Inst) {
1541
1583
if (!SrcAddr)
1542
1584
return false ;
1543
1585
1544
- // If the box has escaped at this instruction, we can't safely promote the
1545
- // load.
1546
- if (DataflowContext. hasEscapedAt (Inst ))
1586
+ SmallVector<AvailableValue, 8 > AvailableValues;
1587
+ auto Result = computeAvailableValues (SrcAddr, Inst, AvailableValues);
1588
+ if (!Result. hasValue ( ))
1547
1589
return false ;
1548
1590
1549
- SILType LoadTy = SrcAddr->getType ().getObjectType ();
1591
+ SILType LoadTy = Result->first ;
1592
+ unsigned FirstElt = Result->second ;
1550
1593
1551
- // If this is a load/copy_addr from a struct field that we want to promote,
1552
- // compute the access path down to the field so we can determine precise
1553
- // def/use behavior.
1554
- unsigned FirstElt = computeSubelement (SrcAddr, TheMemory);
1594
+ // Aggregate together all of the subelements into something that has the same
1595
+ // type as the load did, and emit smaller loads for any subelements that were
1596
+ // not available. We are "propagating" a +1 available value from the store
1597
+ // points.
1598
+ auto *load = dyn_cast<SingleValueInstruction>(Inst);
1599
+ AvailableValueAggregator agg (load, AvailableValues, Uses, deadEndBlocks,
1600
+ false /* isTake*/ );
1601
+ SILValue newVal = agg.aggregateValues (LoadTy, load->getOperand (0 ), FirstElt);
1555
1602
1556
- // If this is a load from within an enum projection, we can't promote it since
1557
- // we don't track subelements in a type that could be changing.
1558
- if (FirstElt == ~0U )
1603
+ LLVM_DEBUG (llvm::dbgs () << " *** Promoting load: " << *load << " \n " );
1604
+ LLVM_DEBUG (llvm::dbgs () << " To value: " << *newVal << " \n " );
1605
+
1606
+ // If we inserted any copies, we created the copies at our stores. We know
1607
+ // that in our load block, we will reform the aggregate as appropriate at the
1608
+ // load implying that the value /must/ be fully consumed. If we promoted a +0
1609
+ // value, we created dominating destroys along those paths. Thus any leaking
1610
+ // blocks that we may have can be found by performing a linear lifetime check
1611
+ // over all copies that we found using the load as the "consuming uses" (just
1612
+ // for the purposes of identifying the consuming block).
1613
+ auto *oldLoad = agg.addMissingDestroysForCopiedValues (load, newVal);
1614
+
1615
+ ++NumLoadPromoted;
1616
+
1617
+ // If we are returned the load, eliminate it. Otherwise, it was already
1618
+ // handled for us... so return true.
1619
+ if (!oldLoad)
1620
+ return true ;
1621
+
1622
+ oldLoad->replaceAllUsesWith (newVal);
1623
+ SILValue addr = oldLoad->getOperand (0 );
1624
+ oldLoad->eraseFromParent ();
1625
+ if (auto *addrI = addr->getDefiningInstruction ())
1626
+ recursivelyDeleteTriviallyDeadInstructions (addrI);
1627
+ return true ;
1628
+ }
1629
+
1630
+ bool AllocOptimize::promoteCopyAddr (CopyAddrInst *Inst) {
1631
+ // Note that we intentionally don't support forwarding of weak pointers,
1632
+ // because the underlying value may drop be deallocated at any time. We would
1633
+ // have to prove that something in this function is holding the weak value
1634
+ // live across the promoted region and that isn't desired for a stable
1635
+ // diagnostics pass this like one.
1636
+
1637
+ // First attempt to find a source addr for our "load" instruction. If we fail
1638
+ // to find a valid value, just return.
1639
+ SILValue SrcAddr = tryFindSrcAddrForLoad (Inst);
1640
+ if (!SrcAddr)
1559
1641
return false ;
1560
-
1561
- unsigned NumLoadSubElements = getNumSubElements (LoadTy, Module);
1562
-
1563
- // Set up the bitvector of elements being demanded by the load.
1564
- SmallBitVector RequiredElts (NumMemorySubElements);
1565
- RequiredElts.set (FirstElt, FirstElt+NumLoadSubElements);
1566
1642
1567
1643
SmallVector<AvailableValue, 8 > AvailableValues;
1568
- AvailableValues.resize (NumMemorySubElements);
1569
-
1570
- // Find out if we have any available values. If no bits are demanded, we
1571
- // trivially succeed. This can happen when there is a load of an empty struct.
1572
- if (NumLoadSubElements != 0 &&
1573
- !DataflowContext.computeAvailableValues (
1574
- Inst, FirstElt, NumLoadSubElements, RequiredElts, AvailableValues))
1644
+ auto Result = computeAvailableValues (SrcAddr, Inst, AvailableValues);
1645
+ if (!Result.hasValue ())
1575
1646
return false ;
1576
1647
1577
1648
// Ok, we have some available values. If we have a copy_addr, explode it now,
1578
1649
// exposing the load operation within it. Subsequent optimization passes will
1579
1650
// see the load and propagate the available values into it.
1580
- if (auto *CAI = dyn_cast<CopyAddrInst>(Inst)) {
1581
- DataflowContext.explodeCopyAddr (CAI);
1651
+ DataflowContext.explodeCopyAddr (Inst);
1652
+
1653
+ // This is removing the copy_addr, but explodeCopyAddr takes care of
1654
+ // removing the instruction from Uses for us, so we return false.
1655
+ return false ;
1656
+ }
1582
1657
1583
- // This is removing the copy_addr, but explodeCopyAddr takes care of
1584
- // removing the instruction from Uses for us, so we return false.
1658
+ // / At this point, we know that this element satisfies the definitive init
1659
+ // / requirements, so we can try to promote loads to enable SSA-based dataflow
1660
+ // / analysis. We know that accesses to this element only access this element,
1661
+ // / cross element accesses have been scalarized.
1662
+ // /
1663
+ // / This returns true if the load has been removed from the program.
1664
+ bool AllocOptimize::promoteLoadBorrow (LoadBorrowInst *Inst) {
1665
+ // Note that we intentionally don't support forwarding of weak pointers,
1666
+ // because the underlying value may drop be deallocated at any time. We would
1667
+ // have to prove that something in this function is holding the weak value
1668
+ // live across the promoted region and that isn't desired for a stable
1669
+ // diagnostics pass this like one.
1670
+
1671
+ // First attempt to find a source addr for our "load" instruction. If we fail
1672
+ // to find a valid value, just return.
1673
+ SILValue SrcAddr = tryFindSrcAddrForLoad (Inst);
1674
+ if (!SrcAddr)
1585
1675
return false ;
1586
- }
1587
1676
1588
- assert ((isa<LoadBorrowInst>(Inst) || isa<LoadInst>(Inst)) &&
1589
- " Unhandled instruction for this code path!" );
1677
+ SmallVector<AvailableValue, 8 > AvailableValues;
1678
+ auto Result = computeAvailableValues (SrcAddr, Inst, AvailableValues);
1679
+ if (!Result.hasValue ())
1680
+ return false ;
1681
+
1682
+ SILType LoadTy = Result->first ;
1683
+ unsigned FirstElt = Result->second ;
1590
1684
1591
1685
// Aggregate together all of the subelements into something that has the same
1592
1686
// type as the load did, and emit smaller loads for any subelements that were
@@ -1618,10 +1712,9 @@ bool AllocOptimize::promoteLoadCopy(SILInstruction *Inst) {
1618
1712
1619
1713
// If our load was a +0 value, borrow the value and the RAUW. We reuse the
1620
1714
// end_borrows of our load_borrow.
1621
- if (isa<LoadBorrowInst>(oldLoad)) {
1622
- newVal = SILBuilderWithScope (oldLoad).createBeginBorrow (oldLoad->getLoc (),
1623
- newVal);
1624
- }
1715
+ newVal =
1716
+ SILBuilderWithScope (oldLoad).createBeginBorrow (oldLoad->getLoc (), newVal);
1717
+
1625
1718
oldLoad->replaceAllUsesWith (newVal);
1626
1719
SILValue addr = oldLoad->getOperand (0 );
1627
1720
oldLoad->eraseFromParent ();
@@ -1954,9 +2047,28 @@ bool AllocOptimize::optimizeMemoryAccesses() {
1954
2047
auto &use = Uses[i];
1955
2048
// Ignore entries for instructions that got expanded along the way.
1956
2049
if (use.Inst && use.Kind == PMOUseKind::Load) {
1957
- if (promoteLoadCopy (use.Inst )) {
1958
- Uses[i].Inst = nullptr ; // remove entry if load got deleted.
1959
- changed = true ;
2050
+ if (auto *cai = dyn_cast<CopyAddrInst>(use.Inst )) {
2051
+ if (promoteCopyAddr (cai)) {
2052
+ Uses[i].Inst = nullptr ; // remove entry if load got deleted.
2053
+ changed = true ;
2054
+ }
2055
+ continue ;
2056
+ }
2057
+
2058
+ if (auto *lbi = dyn_cast<LoadBorrowInst>(use.Inst )) {
2059
+ if (promoteLoadBorrow (lbi)) {
2060
+ Uses[i].Inst = nullptr ; // remove entry if load got deleted.
2061
+ changed = true ;
2062
+ }
2063
+ continue ;
2064
+ }
2065
+
2066
+ if (auto *li = dyn_cast<LoadInst>(use.Inst )) {
2067
+ if (promoteLoadCopy (li)) {
2068
+ Uses[i].Inst = nullptr ; // remove entry if load got deleted.
2069
+ changed = true ;
2070
+ }
2071
+ continue ;
1960
2072
}
1961
2073
}
1962
2074
}
0 commit comments