Skip to content

Commit 939d51b

Browse files
authored
Merge pull request #22044 from gottesmm/pr-992dae7014e54d95a0f58dda3a09b3baec32aa4f
[pmo] Update predictable dead alloc elimination for ownership.
2 parents 6da0389 + 9328277 commit 939d51b

File tree

2 files changed

+565
-62
lines changed

2 files changed

+565
-62
lines changed

lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp

Lines changed: 149 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ class AvailableValueAggregator {
394394
MutableArrayRef<AvailableValue> AvailableValueList;
395395
SmallVectorImpl<PMOMemoryUse> &Uses;
396396
DeadEndBlocks &deadEndBlocks;
397+
bool isTake;
397398

398399
/// Keep track of all instructions that we have added. Once we are done
399400
/// promoting a value, we need to make sure that if we need to balance any
@@ -405,10 +406,10 @@ class AvailableValueAggregator {
405406
AvailableValueAggregator(SILInstruction *Inst,
406407
MutableArrayRef<AvailableValue> AvailableValueList,
407408
SmallVectorImpl<PMOMemoryUse> &Uses,
408-
DeadEndBlocks &deadEndBlocks)
409+
DeadEndBlocks &deadEndBlocks, bool isTake)
409410
: M(Inst->getModule()), B(Inst), Loc(Inst->getLoc()),
410411
AvailableValueList(AvailableValueList), Uses(Uses),
411-
deadEndBlocks(deadEndBlocks) {}
412+
deadEndBlocks(deadEndBlocks), isTake(isTake) {}
412413

413414
// This is intended to be passed by reference only once constructed.
414415
AvailableValueAggregator(const AvailableValueAggregator &) = delete;
@@ -417,7 +418,13 @@ class AvailableValueAggregator {
417418
operator=(const AvailableValueAggregator &) = delete;
418419
AvailableValueAggregator &operator=(AvailableValueAggregator &&) = delete;
419420

420-
SILValue aggregateValues(SILType LoadTy, SILValue Address, unsigned FirstElt);
421+
SILValue aggregateValues(SILType LoadTy, SILValue Address, unsigned FirstElt,
422+
bool isTopLevel = true);
423+
bool canTake(SILType loadTy, unsigned firstElt) const;
424+
425+
/// If as a result of us copying values, we may have unconsumed destroys, find
426+
/// the appropriate location and place the values there. Only used when
427+
/// ownership is enabled.
421428
void addMissingDestroysForCopiedValues(LoadInst *li);
422429

423430
void print(llvm::raw_ostream &os) const;
@@ -465,11 +472,58 @@ bool AvailableValueAggregator::isFullyAvailable(SILType loadTy,
465472
});
466473
}
467474

475+
// We can only take if we never have to split a larger value to promote this
476+
// address.
477+
bool AvailableValueAggregator::canTake(SILType loadTy,
478+
unsigned firstElt) const {
479+
// If we do not have ownership, we can always take since we do not need to
480+
// keep any ownership invariants up to date. In the future, we should be able
481+
// to chop up larger values before they are being stored.
482+
if (!B.hasOwnership())
483+
return true;
484+
485+
// If we are trivially fully available, just return true.
486+
if (isFullyAvailable(loadTy, firstElt))
487+
return true;
488+
489+
// Otherwise see if we are an aggregate with fully available leaf types.
490+
if (TupleType *tt = loadTy.getAs<TupleType>()) {
491+
return llvm::all_of(indices(tt->getElements()), [&](unsigned eltNo) {
492+
SILType eltTy = loadTy.getTupleElementType(eltNo);
493+
unsigned numSubElt = getNumSubElements(eltTy, M);
494+
bool success = canTake(eltTy, firstElt);
495+
firstElt += numSubElt;
496+
return success;
497+
});
498+
}
499+
500+
if (auto *sd = getFullyReferenceableStruct(loadTy)) {
501+
return llvm::all_of(sd->getStoredProperties(), [&](VarDecl *decl) -> bool {
502+
SILType eltTy = loadTy.getFieldType(decl, M);
503+
unsigned numSubElt = getNumSubElements(eltTy, M);
504+
bool success = canTake(eltTy, firstElt);
505+
firstElt += numSubElt;
506+
return success;
507+
});
508+
}
509+
510+
// Otherwise, fail. The value is not fully available at its leafs. We can not
511+
// perform a take.
512+
return false;
513+
}
514+
468515
/// Given a bunch of primitive subelement values, build out the right aggregate
469516
/// type (LoadTy) by emitting tuple and struct instructions as necessary.
470517
SILValue AvailableValueAggregator::aggregateValues(SILType LoadTy,
471518
SILValue Address,
472-
unsigned FirstElt) {
519+
unsigned FirstElt,
520+
bool isTopLevel) {
521+
// If we are performing a take, make sure that we have available values for
522+
// /all/ of our values. Otherwise, bail.
523+
if (isTopLevel && isTake && !canTake(LoadTy, FirstElt)) {
524+
return SILValue();
525+
}
526+
473527
// Check to see if the requested value is fully available, as an aggregate.
474528
// This is a super-common case for single-element structs, but is also a
475529
// general answer for arbitrary structs and tuples as well.
@@ -515,6 +569,10 @@ AvailableValueAggregator::aggregateFullyAvailableValue(SILType loadTy,
515569
// Use the scope and location of the store at the insertion point.
516570
SILBuilderWithScope builder(insertPts[0], &insertedInsts);
517571
SILLocation loc = insertPts[0]->getLoc();
572+
// If we have a take, just return the value.
573+
if (isTake)
574+
return firstVal.getValue();
575+
// Otherwise, return a copy of the value.
518576
return builder.emitCopyValueOperation(loc, firstVal.getValue());
519577
}
520578

@@ -524,11 +582,19 @@ AvailableValueAggregator::aggregateFullyAvailableValue(SILType loadTy,
524582
// implying that we can just copy firstVal at each insertion point.
525583
SILSSAUpdater updater(B.getModule());
526584
updater.Initialize(loadTy);
527-
for (auto *insertPt : firstVal.getInsertionPoints()) {
585+
586+
for (auto *insertPt : insertPts) {
528587
// Use the scope and location of the store at the insertion point.
529588
SILBuilderWithScope builder(insertPt, &insertedInsts);
530589
SILLocation loc = insertPt->getLoc();
531-
SILValue eltVal = builder.emitCopyValueOperation(loc, firstVal.getValue());
590+
SILValue eltVal = firstVal.getValue();
591+
592+
// If we are not taking, copy the element value.
593+
if (!isTake) {
594+
eltVal = builder.emitCopyValueOperation(loc, eltVal);
595+
}
596+
597+
// And then put the value into the SSA updater.
532598
updater.AddAvailableValue(insertPt->getParent(), eltVal);
533599
}
534600

@@ -551,38 +617,45 @@ SILValue AvailableValueAggregator::aggregateTupleSubElts(TupleType *TT,
551617
// If we are missing any of the available values in this struct element,
552618
// compute an address to load from.
553619
SILValue EltAddr;
554-
if (anyMissing(FirstElt, NumSubElt, AvailableValueList))
620+
if (anyMissing(FirstElt, NumSubElt, AvailableValueList)) {
621+
assert(!isTake && "When taking, values should never be missing?!");
555622
EltAddr =
556623
B.createTupleElementAddr(Loc, Address, EltNo, EltTy.getAddressType());
624+
}
557625

558-
ResultElts.push_back(aggregateValues(EltTy, EltAddr, FirstElt));
626+
ResultElts.push_back(
627+
aggregateValues(EltTy, EltAddr, FirstElt, /*isTopLevel*/ false));
559628
FirstElt += NumSubElt;
560629
}
561630

562631
return B.createTuple(Loc, LoadTy, ResultElts);
563632
}
564633

565-
SILValue AvailableValueAggregator::aggregateStructSubElts(StructDecl *SD,
566-
SILType LoadTy,
567-
SILValue Address,
568-
unsigned FirstElt) {
569-
SmallVector<SILValue, 4> ResultElts;
634+
SILValue AvailableValueAggregator::aggregateStructSubElts(StructDecl *sd,
635+
SILType loadTy,
636+
SILValue address,
637+
unsigned firstElt) {
638+
SmallVector<SILValue, 4> resultElts;
570639

571-
for (auto *FD : SD->getStoredProperties()) {
572-
SILType EltTy = LoadTy.getFieldType(FD, M);
573-
unsigned NumSubElt = getNumSubElements(EltTy, M);
640+
for (auto *decl : sd->getStoredProperties()) {
641+
SILType eltTy = loadTy.getFieldType(decl, M);
642+
unsigned numSubElt = getNumSubElements(eltTy, M);
574643

575644
// If we are missing any of the available values in this struct element,
576645
// compute an address to load from.
577-
SILValue EltAddr;
578-
if (anyMissing(FirstElt, NumSubElt, AvailableValueList))
579-
EltAddr =
580-
B.createStructElementAddr(Loc, Address, FD, EltTy.getAddressType());
646+
SILValue eltAddr;
647+
if (anyMissing(firstElt, numSubElt, AvailableValueList)) {
648+
assert(!isTake && "When taking, values should never be missing?!");
649+
eltAddr =
650+
B.createStructElementAddr(Loc, address, decl, eltTy.getAddressType());
651+
}
581652

582-
ResultElts.push_back(aggregateValues(EltTy, EltAddr, FirstElt));
583-
FirstElt += NumSubElt;
653+
resultElts.push_back(
654+
aggregateValues(eltTy, eltAddr, firstElt, /*isTopLevel*/ false));
655+
firstElt += numSubElt;
584656
}
585-
return B.createStruct(Loc, LoadTy, ResultElts);
657+
658+
return B.createStruct(Loc, loadTy, resultElts);
586659
}
587660

588661
// We have looked through all of the aggregate values and finally found a
@@ -595,6 +668,7 @@ SILValue AvailableValueAggregator::handlePrimitiveValue(SILType loadTy,
595668

596669
// If the value is not available, load the value and update our use list.
597670
if (!val) {
671+
assert(!isTake && "Should only take fully available values?!");
598672
LoadInst *load = ([&]() {
599673
if (B.hasOwnership()) {
600674
return B.createTrivialLoadOr(Loc, address,
@@ -1178,10 +1252,10 @@ class AllocOptimize {
11781252

11791253
private:
11801254
bool promoteLoad(SILInstruction *Inst);
1181-
void promoteDestroyAddr(DestroyAddrInst *DAI,
1182-
MutableArrayRef<AvailableValue> Values);
1183-
bool canPromoteDestroyAddr(DestroyAddrInst *DAI,
1184-
SmallVectorImpl<AvailableValue> &AvailableValues);
1255+
void promoteDestroyAddr(DestroyAddrInst *dai,
1256+
MutableArrayRef<AvailableValue> values);
1257+
bool canPromoteDestroyAddr(DestroyAddrInst *dai,
1258+
SmallVectorImpl<AvailableValue> &availableValues);
11851259
};
11861260

11871261
} // end anonymous namespace
@@ -1272,7 +1346,8 @@ bool AllocOptimize::promoteLoad(SILInstruction *Inst) {
12721346
// type as the load did, and emit smaller loads for any subelements that were
12731347
// not available.
12741348
auto *Load = cast<LoadInst>(Inst);
1275-
AvailableValueAggregator Agg(Load, AvailableValues, Uses, deadEndBlocks);
1349+
AvailableValueAggregator Agg(Load, AvailableValues, Uses, deadEndBlocks,
1350+
false /*isTake*/);
12761351
SILValue NewVal = Agg.aggregateValues(LoadTy, Load->getOperand(), FirstElt);
12771352

12781353
// If we inserted any copies, we created the copies at our stores. We know
@@ -1299,78 +1374,90 @@ bool AllocOptimize::promoteLoad(SILInstruction *Inst) {
12991374

13001375
/// Return true if we can promote the given destroy.
13011376
bool AllocOptimize::canPromoteDestroyAddr(
1302-
DestroyAddrInst *DAI, SmallVectorImpl<AvailableValue> &AvailableValues) {
1303-
SILValue Address = DAI->getOperand();
1377+
DestroyAddrInst *dai, SmallVectorImpl<AvailableValue> &availableValues) {
1378+
SILValue address = dai->getOperand();
13041379

13051380
// We cannot promote destroys of address-only types, because we can't expose
13061381
// the load.
1307-
SILType LoadTy = Address->getType().getObjectType();
1308-
if (LoadTy.isAddressOnly(Module))
1382+
SILType loadTy = address->getType().getObjectType();
1383+
if (loadTy.isAddressOnly(Module))
13091384
return false;
13101385

13111386
// If the box has escaped at this instruction, we can't safely promote the
13121387
// load.
1313-
if (DataflowContext.hasEscapedAt(DAI))
1388+
if (DataflowContext.hasEscapedAt(dai))
13141389
return false;
13151390

13161391
// Compute the access path down to the field so we can determine precise
13171392
// def/use behavior.
1318-
unsigned FirstElt = computeSubelement(Address, TheMemory);
1319-
assert(FirstElt != ~0U && "destroy within enum projection is not valid");
1320-
unsigned NumLoadSubElements = getNumSubElements(LoadTy, Module);
1393+
unsigned firstElt = computeSubelement(address, TheMemory);
1394+
assert(firstElt != ~0U && "destroy within enum projection is not valid");
1395+
unsigned numLoadSubElements = getNumSubElements(loadTy, Module);
13211396

13221397
// Find out if we have any available values. If no bits are demanded, we
13231398
// trivially succeed. This can happen when there is a load of an empty struct.
1324-
if (NumLoadSubElements == 0)
1399+
if (numLoadSubElements == 0)
13251400
return true;
13261401

13271402
// Set up the bitvector of elements being demanded by the load.
1328-
SmallBitVector RequiredElts(NumMemorySubElements);
1329-
RequiredElts.set(FirstElt, FirstElt + NumLoadSubElements);
1403+
SmallBitVector requiredElts(NumMemorySubElements);
1404+
requiredElts.set(firstElt, firstElt + numLoadSubElements);
13301405

13311406
// Compute our available values. If we do not have any available values,
13321407
// return false. We have nothing further to do.
1333-
SmallVector<AvailableValue, 8> TmpList;
1334-
TmpList.resize(NumMemorySubElements);
1335-
if (!DataflowContext.computeAvailableValues(DAI, FirstElt, NumLoadSubElements,
1336-
RequiredElts, TmpList))
1408+
SmallVector<AvailableValue, 8> tmpList;
1409+
tmpList.resize(NumMemorySubElements);
1410+
if (!DataflowContext.computeAvailableValues(dai, firstElt, numLoadSubElements,
1411+
requiredElts, tmpList))
1412+
return false;
1413+
1414+
// Now check that we can perform a take upon our available values. This
1415+
// implies today that our value is fully available. If the value is not fully
1416+
// available, we would need to split stores to promote this destroy_addr. We
1417+
// do not support that yet.
1418+
AvailableValueAggregator agg(dai, tmpList, Uses, deadEndBlocks,
1419+
true /*isTake*/);
1420+
if (!agg.canTake(loadTy, firstElt))
13371421
return false;
13381422

1339-
// Now that we have our final list, move the temporary lists contents into
1340-
// AvailableValues.
1341-
std::move(TmpList.begin(), TmpList.end(),
1342-
std::back_inserter(AvailableValues));
1423+
// Ok, we can promote this destroy_addr... move the temporary lists contents
1424+
// into the final AvailableValues list.
1425+
std::move(tmpList.begin(), tmpList.end(),
1426+
std::back_inserter(availableValues));
13431427

13441428
return true;
13451429
}
13461430

1347-
/// promoteDestroyAddr - DestroyAddr is a composed operation merging
1348-
/// load+strong_release. If the implicit load's value is available, explode it.
1349-
///
1350-
/// Note that we handle the general case of a destroy_addr of a piece of the
1351-
/// memory object, not just destroy_addrs of the entire thing.
1431+
// DestroyAddr is a composed operation merging load [take] + destroy_value. If
1432+
// the implicit load's value is available, explode it.
1433+
//
1434+
// NOTE: We only do this if we have a fully available value.
1435+
//
1436+
// Note that we handle the general case of a destroy_addr of a piece of the
1437+
// memory object, not just destroy_addrs of the entire thing.
13521438
void AllocOptimize::promoteDestroyAddr(
1353-
DestroyAddrInst *DAI, MutableArrayRef<AvailableValue> AvailableValues) {
1354-
SILValue Address = DAI->getOperand();
1355-
SILType LoadTy = Address->getType().getObjectType();
1439+
DestroyAddrInst *dai, MutableArrayRef<AvailableValue> availableValues) {
1440+
SILValue address = dai->getOperand();
1441+
SILType loadTy = address->getType().getObjectType();
13561442

13571443
// Compute the access path down to the field so we can determine precise
13581444
// def/use behavior.
1359-
unsigned FirstElt = computeSubelement(Address, TheMemory);
1445+
unsigned firstElt = computeSubelement(address, TheMemory);
13601446

13611447
// Aggregate together all of the subelements into something that has the same
13621448
// type as the load did, and emit smaller) loads for any subelements that were
13631449
// not available.
1364-
AvailableValueAggregator Agg(DAI, AvailableValues, Uses, deadEndBlocks);
1365-
SILValue NewVal = Agg.aggregateValues(LoadTy, Address, FirstElt);
1450+
AvailableValueAggregator agg(dai, availableValues, Uses, deadEndBlocks,
1451+
true /*isTake*/);
1452+
SILValue newVal = agg.aggregateValues(loadTy, address, firstElt);
13661453

13671454
++NumDestroyAddrPromoted;
13681455

1369-
LLVM_DEBUG(llvm::dbgs() << " *** Promoting destroy_addr: " << *DAI << "\n");
1370-
LLVM_DEBUG(llvm::dbgs() << " To value: " << *NewVal << "\n");
1456+
LLVM_DEBUG(llvm::dbgs() << " *** Promoting destroy_addr: " << *dai << "\n");
1457+
LLVM_DEBUG(llvm::dbgs() << " To value: " << *newVal << "\n");
13711458

1372-
SILBuilderWithScope(DAI).emitDestroyValueOperation(DAI->getLoc(), NewVal);
1373-
DAI->eraseFromParent();
1459+
SILBuilderWithScope(dai).emitDestroyValueOperation(dai->getLoc(), newVal);
1460+
dai->eraseFromParent();
13741461
}
13751462

13761463
namespace {

0 commit comments

Comments
 (0)