Skip to content

Commit 933af09

Browse files
committed
[pred-memopt] Split promoteLoadCopy into 3 methods, one for copy_addr, load, load_borrow.
NFC. This just simplifies the code and makes it easier to reason about. I debried a little bit as well, but nothing too much.
1 parent 8e4202d commit 933af09

File tree

1 file changed

+166
-16
lines changed

1 file changed

+166
-16
lines changed

lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp

Lines changed: 166 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1489,7 +1489,9 @@ class AllocOptimize {
14891489
bool tryToRemoveDeadAllocation();
14901490

14911491
private:
1492-
bool promoteLoadCopy(SILInstruction *Inst);
1492+
bool promoteLoadCopy(LoadInst *Inst);
1493+
bool promoteLoadBorrow(LoadBorrowInst *Inst);
1494+
bool promoteCopyAddr(CopyAddrInst *CAI);
14931495
void promoteLoadTake(LoadInst *Inst, MutableArrayRef<AvailableValue> values);
14941496
void promoteDestroyAddr(DestroyAddrInst *dai,
14951497
MutableArrayRef<AvailableValue> values);
@@ -1528,7 +1530,89 @@ static SILValue tryFindSrcAddrForLoad(SILInstruction *i) {
15281530
/// cross element accesses have been scalarized.
15291531
///
15301532
/// This returns true if the load has been removed from the program.
1531-
bool AllocOptimize::promoteLoadCopy(SILInstruction *Inst) {
1533+
bool AllocOptimize::promoteLoadCopy(LoadInst *Inst) {
1534+
// Note that we intentionally don't support forwarding of weak pointers,
1535+
// because the underlying value may drop be deallocated at any time. We would
1536+
// have to prove that something in this function is holding the weak value
1537+
// live across the promoted region and that isn't desired for a stable
1538+
// diagnostics pass this like one.
1539+
1540+
// First attempt to find a source addr for our "load" instruction. If we fail
1541+
// to find a valid value, just return.
1542+
SILValue SrcAddr = tryFindSrcAddrForLoad(Inst);
1543+
if (!SrcAddr)
1544+
return false;
1545+
1546+
// If the box has escaped at this instruction, we can't safely promote the
1547+
// load.
1548+
if (DataflowContext.hasEscapedAt(Inst))
1549+
return false;
1550+
1551+
SILType LoadTy = SrcAddr->getType().getObjectType();
1552+
1553+
// If this is a load/copy_addr from a struct field that we want to promote,
1554+
// compute the access path down to the field so we can determine precise
1555+
// def/use behavior.
1556+
unsigned FirstElt = computeSubelement(SrcAddr, TheMemory);
1557+
1558+
// If this is a load from within an enum projection, we can't promote it since
1559+
// we don't track subelements in a type that could be changing.
1560+
if (FirstElt == ~0U)
1561+
return false;
1562+
1563+
unsigned NumLoadSubElements = getNumSubElements(LoadTy, Module);
1564+
1565+
// Set up the bitvector of elements being demanded by the load.
1566+
SmallBitVector RequiredElts(NumMemorySubElements);
1567+
RequiredElts.set(FirstElt, FirstElt + NumLoadSubElements);
1568+
1569+
SmallVector<AvailableValue, 8> AvailableValues;
1570+
AvailableValues.resize(NumMemorySubElements);
1571+
1572+
// Find out if we have any available values. If no bits are demanded, we
1573+
// trivially succeed. This can happen when there is a load of an empty struct.
1574+
if (NumLoadSubElements != 0 &&
1575+
!DataflowContext.computeAvailableValues(
1576+
Inst, FirstElt, NumLoadSubElements, RequiredElts, AvailableValues))
1577+
return false;
1578+
1579+
// Aggregate together all of the subelements into something that has the same
1580+
// type as the load did, and emit smaller loads for any subelements that were
1581+
// not available. We are "propagating" a +1 available value from the store
1582+
// points.
1583+
auto *load = dyn_cast<SingleValueInstruction>(Inst);
1584+
AvailableValueAggregator agg(load, AvailableValues, Uses, deadEndBlocks,
1585+
false /*isTake*/);
1586+
SILValue newVal = agg.aggregateValues(LoadTy, load->getOperand(0), FirstElt);
1587+
1588+
LLVM_DEBUG(llvm::dbgs() << " *** Promoting load: " << *load << "\n");
1589+
LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal << "\n");
1590+
1591+
// If we inserted any copies, we created the copies at our stores. We know
1592+
// that in our load block, we will reform the aggregate as appropriate at the
1593+
// load implying that the value /must/ be fully consumed. If we promoted a +0
1594+
// value, we created dominating destroys along those paths. Thus any leaking
1595+
// blocks that we may have can be found by performing a linear lifetime check
1596+
// over all copies that we found using the load as the "consuming uses" (just
1597+
// for the purposes of identifying the consuming block).
1598+
auto *oldLoad = agg.addMissingDestroysForCopiedValues(load, newVal);
1599+
1600+
++NumLoadPromoted;
1601+
1602+
// If we are returned the load, eliminate it. Otherwise, it was already
1603+
// handled for us... so return true.
1604+
if (!oldLoad)
1605+
return true;
1606+
1607+
oldLoad->replaceAllUsesWith(newVal);
1608+
SILValue addr = oldLoad->getOperand(0);
1609+
oldLoad->eraseFromParent();
1610+
if (auto *addrI = addr->getDefiningInstruction())
1611+
recursivelyDeleteTriviallyDeadInstructions(addrI);
1612+
return true;
1613+
}
1614+
1615+
bool AllocOptimize::promoteCopyAddr(CopyAddrInst *Inst) {
15321616
// Note that we intentionally don't support forwarding of weak pointers,
15331617
// because the underlying value may drop be deallocated at any time. We would
15341618
// have to prove that something in this function is holding the weak value
@@ -1577,16 +1661,64 @@ bool AllocOptimize::promoteLoadCopy(SILInstruction *Inst) {
15771661
// Ok, we have some available values. If we have a copy_addr, explode it now,
15781662
// exposing the load operation within it. Subsequent optimization passes will
15791663
// see the load and propagate the available values into it.
1580-
if (auto *CAI = dyn_cast<CopyAddrInst>(Inst)) {
1581-
DataflowContext.explodeCopyAddr(CAI);
1664+
DataflowContext.explodeCopyAddr(Inst);
1665+
1666+
// This is removing the copy_addr, but explodeCopyAddr takes care of
1667+
// removing the instruction from Uses for us, so we return false.
1668+
return false;
1669+
}
1670+
1671+
/// At this point, we know that this element satisfies the definitive init
1672+
/// requirements, so we can try to promote loads to enable SSA-based dataflow
1673+
/// analysis. We know that accesses to this element only access this element,
1674+
/// cross element accesses have been scalarized.
1675+
///
1676+
/// This returns true if the load has been removed from the program.
1677+
bool AllocOptimize::promoteLoadBorrow(LoadBorrowInst *Inst) {
1678+
// Note that we intentionally don't support forwarding of weak pointers,
1679+
// because the underlying value may drop be deallocated at any time. We would
1680+
// have to prove that something in this function is holding the weak value
1681+
// live across the promoted region and that isn't desired for a stable
1682+
// diagnostics pass this like one.
1683+
1684+
// First attempt to find a source addr for our "load" instruction. If we fail
1685+
// to find a valid value, just return.
1686+
SILValue SrcAddr = tryFindSrcAddrForLoad(Inst);
1687+
if (!SrcAddr)
1688+
return false;
15821689

1583-
// This is removing the copy_addr, but explodeCopyAddr takes care of
1584-
// removing the instruction from Uses for us, so we return false.
1690+
// If the box has escaped at this instruction, we can't safely promote the
1691+
// load.
1692+
if (DataflowContext.hasEscapedAt(Inst))
1693+
return false;
1694+
1695+
SILType LoadTy = SrcAddr->getType().getObjectType();
1696+
1697+
// If this is a load/copy_addr from a struct field that we want to promote,
1698+
// compute the access path down to the field so we can determine precise
1699+
// def/use behavior.
1700+
unsigned FirstElt = computeSubelement(SrcAddr, TheMemory);
1701+
1702+
// If this is a load from within an enum projection, we can't promote it since
1703+
// we don't track subelements in a type that could be changing.
1704+
if (FirstElt == ~0U)
15851705
return false;
1586-
}
15871706

1588-
assert((isa<LoadBorrowInst>(Inst) || isa<LoadInst>(Inst)) &&
1589-
"Unhandled instruction for this code path!");
1707+
unsigned NumLoadSubElements = getNumSubElements(LoadTy, Module);
1708+
1709+
// Set up the bitvector of elements being demanded by the load.
1710+
SmallBitVector RequiredElts(NumMemorySubElements);
1711+
RequiredElts.set(FirstElt, FirstElt + NumLoadSubElements);
1712+
1713+
SmallVector<AvailableValue, 8> AvailableValues;
1714+
AvailableValues.resize(NumMemorySubElements);
1715+
1716+
// Find out if we have any available values. If no bits are demanded, we
1717+
// trivially succeed. This can happen when there is a load of an empty struct.
1718+
if (NumLoadSubElements != 0 &&
1719+
!DataflowContext.computeAvailableValues(
1720+
Inst, FirstElt, NumLoadSubElements, RequiredElts, AvailableValues))
1721+
return false;
15901722

15911723
// Aggregate together all of the subelements into something that has the same
15921724
// type as the load did, and emit smaller loads for any subelements that were
@@ -1618,10 +1750,9 @@ bool AllocOptimize::promoteLoadCopy(SILInstruction *Inst) {
16181750

16191751
// If our load was a +0 value, borrow the value and the RAUW. We reuse the
16201752
// end_borrows of our load_borrow.
1621-
if (isa<LoadBorrowInst>(oldLoad)) {
1622-
newVal = SILBuilderWithScope(oldLoad).createBeginBorrow(oldLoad->getLoc(),
1623-
newVal);
1624-
}
1753+
newVal =
1754+
SILBuilderWithScope(oldLoad).createBeginBorrow(oldLoad->getLoc(), newVal);
1755+
16251756
oldLoad->replaceAllUsesWith(newVal);
16261757
SILValue addr = oldLoad->getOperand(0);
16271758
oldLoad->eraseFromParent();
@@ -1954,9 +2085,28 @@ bool AllocOptimize::optimizeMemoryAccesses() {
19542085
auto &use = Uses[i];
19552086
// Ignore entries for instructions that got expanded along the way.
19562087
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;
2088+
if (auto *cai = dyn_cast<CopyAddrInst>(use.Inst)) {
2089+
if (promoteCopyAddr(cai)) {
2090+
Uses[i].Inst = nullptr; // remove entry if load got deleted.
2091+
changed = true;
2092+
}
2093+
continue;
2094+
}
2095+
2096+
if (auto *lbi = dyn_cast<LoadBorrowInst>(use.Inst)) {
2097+
if (promoteLoadBorrow(lbi)) {
2098+
Uses[i].Inst = nullptr; // remove entry if load got deleted.
2099+
changed = true;
2100+
}
2101+
continue;
2102+
}
2103+
2104+
if (auto *li = dyn_cast<LoadInst>(use.Inst)) {
2105+
if (promoteLoadCopy(li)) {
2106+
Uses[i].Inst = nullptr; // remove entry if load got deleted.
2107+
changed = true;
2108+
}
2109+
continue;
19602110
}
19612111
}
19622112
}

0 commit comments

Comments
 (0)