Skip to content

Commit 13cdeff

Browse files
authored
Merge pull request #28046 from gottesmm/pr-a32758bd7b10e19ef04df36d15343722c0c115a7
2 parents 8e4202d + fad176e commit 13cdeff

File tree

1 file changed

+152
-40
lines changed

1 file changed

+152
-40
lines changed

lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp

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

14911491
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);
14931498
void promoteLoadTake(LoadInst *Inst, MutableArrayRef<AvailableValue> values);
14941499
void promoteDestroyAddr(DestroyAddrInst *dai,
14951500
MutableArrayRef<AvailableValue> values);
@@ -1499,6 +1504,43 @@ class AllocOptimize {
14991504

15001505
} // end anonymous namespace
15011506

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+
}
15021544

15031545
/// If we are able to optimize \p Inst, return the source address that
15041546
/// instruction is loading from. If we can not optimize \p Inst, then just
@@ -1528,7 +1570,7 @@ static SILValue tryFindSrcAddrForLoad(SILInstruction *i) {
15281570
/// cross element accesses have been scalarized.
15291571
///
15301572
/// This returns true if the load has been removed from the program.
1531-
bool AllocOptimize::promoteLoadCopy(SILInstruction *Inst) {
1573+
bool AllocOptimize::promoteLoadCopy(LoadInst *Inst) {
15321574
// Note that we intentionally don't support forwarding of weak pointers,
15331575
// because the underlying value may drop be deallocated at any time. We would
15341576
// have to prove that something in this function is holding the weak value
@@ -1541,52 +1583,104 @@ bool AllocOptimize::promoteLoadCopy(SILInstruction *Inst) {
15411583
if (!SrcAddr)
15421584
return false;
15431585

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())
15471589
return false;
15481590

1549-
SILType LoadTy = SrcAddr->getType().getObjectType();
1591+
SILType LoadTy = Result->first;
1592+
unsigned FirstElt = Result->second;
15501593

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);
15551602

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)
15591641
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);
15661642

15671643
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())
15751646
return false;
15761647

15771648
// Ok, we have some available values. If we have a copy_addr, explode it now,
15781649
// exposing the load operation within it. Subsequent optimization passes will
15791650
// 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+
}
15821657

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)
15851675
return false;
1586-
}
15871676

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;
15901684

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

16191713
// If our load was a +0 value, borrow the value and the RAUW. We reuse the
16201714
// 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+
16251718
oldLoad->replaceAllUsesWith(newVal);
16261719
SILValue addr = oldLoad->getOperand(0);
16271720
oldLoad->eraseFromParent();
@@ -1954,9 +2047,28 @@ bool AllocOptimize::optimizeMemoryAccesses() {
19542047
auto &use = Uses[i];
19552048
// Ignore entries for instructions that got expanded along the way.
19562049
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;
19602072
}
19612073
}
19622074
}

0 commit comments

Comments
 (0)