Skip to content

Commit 9c34735

Browse files
committed
Don't pass dead values into proactively added unnecessary phis in SILMem2Reg
Add utility to create end_lifetime at leaking blocks for an owned value
1 parent 9c84631 commit 9c34735

File tree

6 files changed

+448
-37
lines changed

6 files changed

+448
-37
lines changed

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,10 @@ makeNewValueAvailable(SILValue value, SILBasicBlock *inBlock);
678678
/// Warning: This does not properly cleanup an OSSA lifetime with a consuming
679679
/// use blocks inside a loop relative to \p value. The client must create
680680
/// separate copies for any uses within the loop.
681+
void destroyValueAtLeakingBlocks(SILValue value,
682+
ArrayRef<SILBasicBlock *> userBBs);
683+
684+
/// Given an ssa value \p value, create end_lifetime at leaking blocks
681685
void endLifetimeAtLeakingBlocks(SILValue value,
682686
ArrayRef<SILBasicBlock *> userBBs);
683687

lib/SILOptimizer/Transforms/RedundantLoadElimination.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1361,7 +1361,7 @@ SILValue RLEContext::computePredecessorLocationValue(SILBasicBlock *BB,
13611361
for (auto use : phi->getUses()) {
13621362
userBBs.push_back(use->getParentBlock());
13631363
}
1364-
endLifetimeAtLeakingBlocks(phi, userBBs);
1364+
destroyValueAtLeakingBlocks(phi, userBBs);
13651365
}
13661366
return makeNewValueAvailable(Val, BB);
13671367
}

lib/SILOptimizer/Transforms/SILMem2Reg.cpp

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -261,14 +261,18 @@ class StackAllocationPromoter {
261261
/// AllocStackInst.
262262
BlockToInstMap lastStoreInBlock;
263263

264+
/// Records blocks in which the value in asi is moved out.
265+
/// i.e There was a load [take] of the asi with no following store.
266+
BasicBlockSet deadValueBlock;
264267
public:
265268
/// C'tor.
266269
StackAllocationPromoter(AllocStackInst *inputASI, DominanceInfo *inputDomInfo,
267270
DomTreeLevelMap &inputDomTreeLevels,
268271
SILBuilderContext &inputCtx,
269272
InstructionDeleter &deleter)
270273
: asi(inputASI), dsi(nullptr), domInfo(inputDomInfo),
271-
domTreeLevels(inputDomTreeLevels), ctx(inputCtx), deleter(deleter) {
274+
domTreeLevels(inputDomTreeLevels), ctx(inputCtx), deleter(deleter),
275+
deadValueBlock(inputASI->getFunction()) {
272276
// Scan the users in search of a deallocation instruction.
273277
for (auto *use : asi->getUses()) {
274278
if (auto *foundDealloc = dyn_cast<DeallocStackInst>(use->getUser())) {
@@ -330,12 +334,14 @@ class StackAllocationPromoter {
330334
StoreInst *StackAllocationPromoter::promoteAllocationInBlock(
331335
SILBasicBlock *blockPromotingWithin) {
332336
LLVM_DEBUG(llvm::dbgs() << "*** Promoting ASI in block: " << *asi);
333-
334337
// RunningVal is the current value in the stack location.
335338
// We don't know the value of the alloca until we find the first store.
336339
SILValue runningVal = SILValue();
337340
// Keep track of the last StoreInst that we found.
338341
StoreInst *lastStore = nullptr;
342+
// Keep track if value in asi becomes dead via load [take] or destroy_addr in
343+
// this block
344+
bool deadAsi = false;
339345

340346
// For all instructions in the block.
341347
for (auto bbi = blockPromotingWithin->begin(),
@@ -346,6 +352,10 @@ StoreInst *StackAllocationPromoter::promoteAllocationInBlock(
346352

347353
if (isLoadFromStack(inst, asi)) {
348354
auto *li = cast<LoadInst>(inst);
355+
// The value in asi was moved out, set deadAsi to true
356+
if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) {
357+
deadAsi = true;
358+
}
349359
if (runningVal) {
350360
// If we are loading from the AllocStackInst and we already know the
351361
// content of the Alloca then use it.
@@ -371,6 +381,9 @@ StoreInst *StackAllocationPromoter::promoteAllocationInBlock(
371381
if (si->getDest() != asi)
372382
continue;
373383

384+
// Found a store to asi, set deadAsi to false.
385+
deadAsi = false;
386+
374387
// If we see a store [assign], always convert it to a store [init]. This
375388
// simplifies further processing.
376389
if (si->getOwnershipQualifier() == StoreOwnershipQualifier::Assign) {
@@ -416,28 +429,28 @@ StoreInst *StackAllocationPromoter::promoteAllocationInBlock(
416429

417430
// Replace destroys with a release of the value.
418431
if (auto *dai = dyn_cast<DestroyAddrInst>(inst)) {
419-
if (dai->getOperand() == asi && runningVal) {
420-
replaceDestroy(dai, runningVal, ctx, deleter);
432+
if (dai->getOperand() == asi) {
433+
if (runningVal) {
434+
replaceDestroy(dai, runningVal, ctx, deleter);
435+
}
436+
// The value in asi was destroyed, set deadAsi to true
437+
deadAsi = true;
421438
}
422439
continue;
423440
}
424441

425-
if (auto *dvi = dyn_cast<DestroyValueInst>(inst)) {
426-
if (dvi->getOperand() == runningVal) {
427-
// Reset LastStore.
428-
// So that we don't end up passing dead values as phi args in
429-
// StackAllocationPromoter::fixBranchesAndUses
430-
lastStore = nullptr;
431-
}
432-
}
433-
434442
// Stop on deallocation.
435443
if (auto *dsi = dyn_cast<DeallocStackInst>(inst)) {
436444
if (dsi->getOperand() == asi)
437445
break;
438446
}
439447
}
440448

449+
// If the asi was dead in this block, insert into deadValueBlock in OSSA.
450+
if (deadAsi && asi->getFunction()->hasOwnership() &&
451+
!asi->getType().isTrivial(*asi->getFunction())) {
452+
deadValueBlock.insert(blockPromotingWithin);
453+
}
441454
if (lastStore) {
442455
assert(lastStore->getOwnershipQualifier() !=
443456
StoreOwnershipQualifier::Assign &&
@@ -461,11 +474,17 @@ void StackAllocationPromoter::addBlockArguments(BlockSetVector &phiBlocks) {
461474
SILValue StackAllocationPromoter::getLiveOutValue(BlockSetVector &phiBlocks,
462475
SILBasicBlock *startBlock) {
463476
LLVM_DEBUG(llvm::dbgs() << "*** Searching for a value definition.\n");
477+
auto *func = asi->getFunction();
478+
464479
// Walk the Dom tree in search of a defining value:
465480
for (DomTreeNode *domNode = domInfo->getNode(startBlock); domNode;
466481
domNode = domNode->getIDom()) {
467482
SILBasicBlock *domBlock = domNode->getBlock();
468483

484+
// If the value in asi is moved out, then return undef
485+
if (deadValueBlock.contains(domBlock)) {
486+
return SILUndef::get(asi->getElementType(), *func);
487+
}
469488
// If there is a store (that must come after the phi), use its value.
470489
BlockToInstMap::iterator it = lastStoreInBlock.find(domBlock);
471490
if (it != lastStoreInBlock.end())
@@ -487,7 +506,7 @@ SILValue StackAllocationPromoter::getLiveOutValue(BlockSetVector &phiBlocks,
487506
LLVM_DEBUG(llvm::dbgs() << "*** Walking up the iDOM.\n");
488507
}
489508
LLVM_DEBUG(llvm::dbgs() << "*** Could not find a Def. Using Undef.\n");
490-
return SILUndef::get(asi->getElementType(), *asi->getFunction());
509+
return SILUndef::get(asi->getElementType(), *func);
491510
}
492511

493512
SILValue StackAllocationPromoter::getLiveInValue(BlockSetVector &phiBlocks,
@@ -596,14 +615,22 @@ void StackAllocationPromoter::fixBranchesAndUses(BlockSetVector &phiBlocks) {
596615
}
597616
}
598617

599-
// If the owned phi arg we added did not have any uses, create end_lifetime to
600-
// end its lifetime. In asserts mode, make sure we have only undef incoming
601-
// values for such phi args.
602-
for (auto *block : phiBlocks) {
603-
auto *phiArg =
604-
cast<SILPhiArgument>(block->getArgument(block->getNumArguments() - 1));
605-
if (phiArg->use_empty()) {
606-
erasePhiArgument(block, block->getNumArguments() - 1);
618+
// Fix ownership of newly created non-trivial phis
619+
if (asi->getFunction()->hasOwnership() &&
620+
!asi->getType().isTrivial(*asi->getFunction())) {
621+
// Go over all the proactively added phis and create end_lifetime at
622+
// leaking blocks
623+
SmallVector<SILBasicBlock *, 4> consumingBlocks;
624+
for (auto *block : phiBlocks) {
625+
auto *phiArg = cast<SILPhiArgument>(
626+
block->getArgument(block->getNumArguments() - 1));
627+
if (phiArg->use_empty()) {
628+
auto *insertPt = phiArg->getNextInstruction();
629+
SILBuilderWithScope builder(insertPt);
630+
builder.createEndLifetime(
631+
RegularLocation::getAutoGeneratedLocation(insertPt->getLoc()),
632+
phiArg);
633+
}
607634
}
608635
}
609636
}
@@ -1049,6 +1076,10 @@ bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc) {
10491076
LLVM_DEBUG(llvm::dbgs() << "*** Memory to register looking at: " << *alloc);
10501077
++NumAllocStackFound;
10511078

1079+
if (f.hasOwnership() && alloc->hasDynamicLifetime()) {
1080+
return false;
1081+
}
1082+
10521083
// Don't handle captured AllocStacks.
10531084
bool inSingleBlock = false;
10541085
if (isCaptured(alloc, inSingleBlock)) {
@@ -1058,9 +1089,8 @@ bool MemoryToRegisters::promoteSingleAllocation(AllocStackInst *alloc) {
10581089

10591090
// Remove write-only AllocStacks.
10601091
if (isWriteOnlyAllocation(alloc)) {
1061-
deleter.forceDeleteWithUsers(alloc);
1062-
10631092
LLVM_DEBUG(llvm::dbgs() << "*** Deleting store-only AllocStack: "<< *alloc);
1093+
deleter.forceDeleteWithUsers(alloc);
10641094
return true;
10651095
}
10661096

lib/SILOptimizer/Utils/InstOptUtils.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2082,6 +2082,25 @@ bool swift::tryEliminateOnlyOwnershipUsedForwardingInst(
20822082

20832083
// The consuming use blocks are assumed either not to inside a loop relative to
20842084
// \p value or they must have their own copies.
2085+
void swift::destroyValueAtLeakingBlocks(SILValue value,
2086+
ArrayRef<SILBasicBlock *> uses) {
2087+
if (!value->getFunction()->hasOwnership())
2088+
return;
2089+
2090+
if (value.getOwnershipKind() != OwnershipKind::Owned)
2091+
return;
2092+
2093+
findJointPostDominatingSet(
2094+
value->getParentBlock(), uses, [&](SILBasicBlock *loopBlock) {},
2095+
[&](SILBasicBlock *postDomBlock) {
2096+
// Insert a destroy_value in the leaking block
2097+
auto front = postDomBlock->begin();
2098+
SILBuilderWithScope newBuilder(front);
2099+
newBuilder.createDestroyValue(
2100+
RegularLocation::getAutoGeneratedLocation(), value);
2101+
});
2102+
}
2103+
20852104
void swift::endLifetimeAtLeakingBlocks(SILValue value,
20862105
ArrayRef<SILBasicBlock *> uses) {
20872106
if (!value->getFunction()->hasOwnership())
@@ -2096,7 +2115,7 @@ void swift::endLifetimeAtLeakingBlocks(SILValue value,
20962115
// Insert a destroy_value in the leaking block
20972116
auto front = postDomBlock->begin();
20982117
SILBuilderWithScope newBuilder(front);
2099-
newBuilder.createDestroyValue(
2118+
newBuilder.createEndLifetime(
21002119
RegularLocation::getAutoGeneratedLocation(), value);
21012120
});
21022121
}

test/SILOptimizer/mem2reg.sil

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -466,11 +466,11 @@ bb0(%0 : $Optional<Klass>):
466466
return %4 : $()
467467
}
468468

469-
// check no dead args are passed to bb3
469+
// dead args okay in non-ossa
470470
// CHECK-LABEL: sil @multi_basic_block_use_on_one_path :
471471
// CHECK-NOT: alloc_stack
472-
// CHECK: bb3:
473-
// CHECK-LABEL: } // end sil function 'multi_basic_block_use_on_one_path'
472+
// CHECK: br bb3(undef : $Klass)
473+
// CHECK: bb3([[dead_arg:%.*]]):
474474
sil @multi_basic_block_use_on_one_path : $@convention(thin) (@owned Klass) -> () {
475475
bb0(%0 : $Klass):
476476
%1 = alloc_stack $Klass

0 commit comments

Comments
 (0)