Skip to content

Commit c920101

Browse files
committed
[addr-move-function] Fix a bug where I was inferring a value from certain destroy_addr to insert compensating destroy_addr.
This doesn't work well if one can potentially not have any input destroy_addr... I actually already know the address without inferring it, so I changed the code to just use that. I also added more tests around defer/without defer that exercise this behavior. (cherry picked from commit b5eca9f)
1 parent 2dc81ed commit c920101

File tree

3 files changed

+187
-23
lines changed

3 files changed

+187
-23
lines changed

lib/SILOptimizer/Mandatory/MoveKillsCopyableAddressesChecker.cpp

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,7 +1364,7 @@ struct DataflowState {
13641364
bool handleSingleBlockClosure(SILArgument *address,
13651365
ClosureOperandState &state);
13661366
bool cleanupAllDestroyAddr(
1367-
SILFunction *fn, SmallBitVector &destroyIndices,
1367+
SILValue address, SILFunction *fn, SmallBitVector &destroyIndices,
13681368
SmallBitVector &reinitIndices, SmallBitVector &consumingClosureIndices,
13691369
BasicBlockSet &blocksVisitedWhenProcessingNewTakes,
13701370
BasicBlockSet &blocksWithMovesThatAreNowTakes,
@@ -1383,14 +1383,14 @@ struct DataflowState {
13831383
} // namespace
13841384

13851385
bool DataflowState::cleanupAllDestroyAddr(
1386-
SILFunction *fn, SmallBitVector &destroyIndices,
1386+
SILValue address, SILFunction *fn, SmallBitVector &destroyIndices,
13871387
SmallBitVector &reinitIndices, SmallBitVector &consumingClosureIndices,
13881388
BasicBlockSet &blocksVisitedWhenProcessingNewTakes,
13891389
BasicBlockSet &blocksWithMovesThatAreNowTakes,
13901390
SmallBlotSetVector<SILInstruction *, 8> &postDominatingConsumingUsers) {
13911391
bool madeChange = false;
13921392
BasicBlockWorklist worklist(fn);
1393-
SILValue daiOperand;
1393+
13941394
LLVM_DEBUG(llvm::dbgs() << "Cleanup up destroy addr!\n");
13951395
LLVM_DEBUG(llvm::dbgs() << " Visiting destroys!\n");
13961396
LLVM_DEBUG(llvm::dbgs() << " Destroy Indices: " << destroyIndices << "\n");
@@ -1401,9 +1401,6 @@ bool DataflowState::cleanupAllDestroyAddr(
14011401
if (!dai)
14021402
continue;
14031403
LLVM_DEBUG(llvm::dbgs() << " Destroy: " << *dai);
1404-
SILValue op = (*dai)->getOperand();
1405-
assert(daiOperand == SILValue() || op == daiOperand);
1406-
daiOperand = op;
14071404
for (auto *predBlock : (*dai)->getParent()->getPredecessorBlocks()) {
14081405
worklist.pushIfNotVisited(predBlock);
14091406
}
@@ -1415,7 +1412,7 @@ bool DataflowState::cleanupAllDestroyAddr(
14151412
auto reinit = useState.reinits[index];
14161413
if (!reinit)
14171414
continue;
1418-
LLVM_DEBUG(llvm::dbgs() << " Reinit: " << *reinit);
1415+
LLVM_DEBUG(llvm::dbgs() << " Reinit: " << **reinit);
14191416
for (auto *predBlock : (*reinit)->getParent()->getPredecessorBlocks()) {
14201417
worklist.pushIfNotVisited(predBlock);
14211418
}
@@ -1436,16 +1433,33 @@ bool DataflowState::cleanupAllDestroyAddr(
14361433
while (auto *next = worklist.pop()) {
14371434
LLVM_DEBUG(llvm::dbgs()
14381435
<< "Looking at block: bb" << next->getDebugID() << "\n");
1436+
14391437
// Any blocks that contained processed moves are stop points.
1440-
if (blocksWithMovesThatAreNowTakes.contains(next))
1438+
if (blocksWithMovesThatAreNowTakes.contains(next)) {
1439+
LLVM_DEBUG(llvm::dbgs()
1440+
<< " Block contained a move that is now a true take.\n");
14411441
continue;
1442+
}
14421443

1444+
// Then if we find that we have a block that was never visited when we
1445+
// walked along successor edges from the move, then we know that we need to
1446+
// insert a destroy_addr.
1447+
//
1448+
// This is safe to do since this block lives along the dominance frontier
1449+
// and we do not allow for critical edges, so as we walk along predecessors,
1450+
// given that any such block must also have a successor that was reachable
1451+
// from our move, we know that this unprocessed block must only have one
1452+
// successor, a block reachable from our move and thus must not have any
1453+
// unhandled uses.
14431454
if (!blocksVisitedWhenProcessingNewTakes.contains(next)) {
1455+
LLVM_DEBUG(llvm::dbgs() << " Found a block that was not visited when "
1456+
"we processed takes of the given move.\n");
14441457
// Insert a destroy_addr here since the block isn't reachable from any of
14451458
// our moves.
1446-
SILBuilderWithScope builder(next->getTerminator());
1459+
SILBuilderWithScope builder(
1460+
std::prev(next->getTerminator()->getIterator()));
14471461
auto *dvi = builder.createDestroyAddr(
1448-
RegularLocation::getAutoGeneratedLocation(), daiOperand);
1462+
RegularLocation::getAutoGeneratedLocation(), address);
14491463
useState.destroys.insert(dvi);
14501464
continue;
14511465
}
@@ -1538,7 +1552,7 @@ bool DataflowState::process(
15381552
return indicesOfPairedConsumingClosureUses;
15391553
};
15401554

1541-
BasicBlockSet visitedByNewMove(fn);
1555+
BasicBlockSet blocksVisitedWhenProcessingNewTakes(fn);
15421556
BasicBlockSet blocksWithMovesThatAreNowTakes(fn);
15431557
bool convertedMarkMoveToTake = false;
15441558
for (auto *mvi : markMovesThatPropagateDownwards) {
@@ -1547,12 +1561,12 @@ bool DataflowState::process(
15471561
LLVM_DEBUG(llvm::dbgs() << "Checking Multi Block Dataflow for: " << *mvi);
15481562

15491563
BasicBlockWorklist worklist(fn);
1550-
BasicBlockSetVector setVector(fn);
1564+
BasicBlockSetVector visitedBlocks(fn);
15511565
for (auto *succBlock : mvi->getParent()->getSuccessorBlocks()) {
15521566
LLVM_DEBUG(llvm::dbgs()
15531567
<< " SuccBlocks: " << succBlock->getDebugID() << "\n");
15541568
worklist.pushIfNotVisited(succBlock);
1555-
setVector.insert(succBlock);
1569+
visitedBlocks.insert(succBlock);
15561570
}
15571571

15581572
while (auto *next = worklist.pop()) {
@@ -1684,7 +1698,7 @@ bool DataflowState::process(
16841698
// Otherwise, add successors if we haven't visited them to the worklist.
16851699
for (auto *succBlock : next->getSuccessorBlocks()) {
16861700
worklist.pushIfNotVisited(succBlock);
1687-
setVector.insert(succBlock);
1701+
visitedBlocks.insert(succBlock);
16881702
}
16891703
}
16901704

@@ -1701,8 +1715,8 @@ bool DataflowState::process(
17011715
builder.createCopyAddr(mvi->getLoc(), mvi->getSrc(), mvi->getDest(),
17021716
IsTake, IsInitialization);
17031717
// Flush our SetVector into the visitedByNewMove.
1704-
for (auto *block : setVector) {
1705-
visitedByNewMove.insert(block);
1718+
for (auto *block : visitedBlocks) {
1719+
blocksVisitedWhenProcessingNewTakes.insert(block);
17061720
}
17071721
convertedMarkMoveToTake = true;
17081722
}
@@ -1716,9 +1730,10 @@ bool DataflowState::process(
17161730
// Now that we have processed all of our mark_moves, eliminate all of the
17171731
// destroy_addr.
17181732
madeChange |= cleanupAllDestroyAddr(
1719-
fn, getIndicesOfPairedDestroys(), getIndicesOfPairedReinits(),
1720-
getIndicesOfPairedConsumingClosureUses(), visitedByNewMove,
1721-
blocksWithMovesThatAreNowTakes, postDominatingConsumingUsers);
1733+
address, fn, getIndicesOfPairedDestroys(), getIndicesOfPairedReinits(),
1734+
getIndicesOfPairedConsumingClosureUses(),
1735+
blocksVisitedWhenProcessingNewTakes, blocksWithMovesThatAreNowTakes,
1736+
postDominatingConsumingUsers);
17221737

17231738
return madeChange;
17241739
}

test/SILOptimizer/move_function_kills_copyable_addressonly_vars.swift

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@ func exchangeUse<T>(_ k: __owned T) -> T { k }
2323

2424
public protocol P {
2525
var k: Klass { get }
26+
27+
static func getP() -> Self
28+
29+
func doSomething()
2630
}
31+
2732
public protocol SubP1 : P {}
2833
public protocol SubP2 : P {}
2934

@@ -214,10 +219,7 @@ struct S<T> {
214219
// Defer Tests //
215220
/////////////////
216221

217-
protocol DeferTestProtocol {
218-
var k: Klass { get }
219-
220-
static func getP() -> Self
222+
protocol DeferTestProtocol : P {
221223
}
222224

223225
extension DeferTestProtocol {
@@ -413,6 +415,37 @@ extension DeferTestProtocol {
413415
}
414416
print("foo bar")
415417
}
418+
419+
mutating func deferTestSuccess13() {
420+
let selfType = type(of: self)
421+
if booleanValue {
422+
print("creating blocks")
423+
} else {
424+
let _ = _move(self)
425+
print("creating blocks2")
426+
}
427+
428+
defer {
429+
self = selfType.getP()
430+
}
431+
print("foo bar")
432+
}
433+
434+
mutating func deferTestSuccess14() {
435+
let selfType = type(of: self)
436+
if booleanValue {
437+
print("creating blocks")
438+
self.doSomething()
439+
} else {
440+
let _ = _move(self)
441+
print("creating blocks2")
442+
}
443+
444+
defer {
445+
self = selfType.getP()
446+
}
447+
print("foo bar")
448+
}
416449
}
417450

418451
////////////////
@@ -553,3 +586,48 @@ public func partialApplyTest<T : P>(_ x: __owned T) {
553586
}
554587
f()
555588
}
589+
590+
////////////////////////
591+
// Misc Tests on Self //
592+
////////////////////////
593+
594+
protocol MiscTests : P {}
595+
596+
extension MiscTests {
597+
598+
// This test makes sure that we are able to properly put in the destroy_addr
599+
// in the "creating blocks" branch. There used to be a bug where the impl
600+
// would need at least one destroy_addr to properly infer the value to put
601+
// into blocks not reachable from the _move but that are on the dominance
602+
// frontier from the _move. This was unnecessary and the test makes sure we
603+
// do not fail on this again.
604+
mutating func noDestroyAddrBeforeOptInsertAfter() {
605+
let selfType = type(of: self)
606+
if booleanValue {
607+
print("creating blocks")
608+
} else {
609+
let _ = _move(self)
610+
print("creating blocks2")
611+
}
612+
613+
self = selfType.getP()
614+
print("foo bar")
615+
}
616+
617+
// A derived version of noDestroyAddrBeforeOptInsertAfter that makes sure
618+
// when we insert the destroy_addr, we destroy self at the end of the block.
619+
mutating func noDestroyAddrBeforeOptInsertAfter2() {
620+
let selfType = type(of: self)
621+
if booleanValue {
622+
print("creating blocks")
623+
self.doSomething()
624+
} else {
625+
let _ = _move(self)
626+
print("creating blocks2")
627+
}
628+
629+
self = selfType.getP()
630+
print("foo bar")
631+
}
632+
}
633+

test/SILOptimizer/move_function_kills_copyable_loadable_vars.swift

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,35 @@ extension KlassWrapper {
463463
}
464464
print("foo bar")
465465
}
466+
467+
mutating func deferTestSuccess13() {
468+
if booleanValue {
469+
print("creating blocks")
470+
} else {
471+
let _ = _move(self)
472+
print("creating blocks2")
473+
}
474+
475+
defer {
476+
self = KlassWrapper(k: Klass())
477+
}
478+
print("foo bar")
479+
}
480+
481+
mutating func deferTestSuccess14() {
482+
if booleanValue {
483+
print("creating blocks")
484+
self.doSomething()
485+
} else {
486+
let _ = _move(self)
487+
print("creating blocks2")
488+
}
489+
490+
defer {
491+
self = KlassWrapper(k: Klass())
492+
}
493+
print("foo bar")
494+
}
466495
}
467496

468497
////////////////
@@ -603,3 +632,45 @@ public func partialApplyTest(_ x: __owned Klass) {
603632
}
604633
f()
605634
}
635+
636+
////////////////////////
637+
// Misc Tests on Self //
638+
////////////////////////
639+
640+
extension KlassWrapper {
641+
642+
func doSomething() { print("foo") }
643+
644+
// This test makes sure that we are able to properly put in the destroy_addr
645+
// in the "creating blocks" branch. There used to be a bug where the impl
646+
// would need at least one destroy_addr to properly infer the value to put
647+
// into blocks not reachable from the _move but that are on the dominance
648+
// frontier from the _move. This was unnecessary and the test makes sure we
649+
// do not fail on this again.
650+
mutating func noDestroyAddrBeforeOptInsertAfter() {
651+
if booleanValue {
652+
print("creating blocks")
653+
} else {
654+
let _ = _move(self)
655+
print("creating blocks2")
656+
}
657+
658+
self = .init(k: Klass())
659+
print("foo bar")
660+
}
661+
662+
// A derived version of noDestroyAddrBeforeOptInsertAfter that makes sure
663+
// when we insert the destroy_addr, we destroy self at the end of the block.
664+
mutating func noDestroyAddrBeforeOptInsertAfter2() {
665+
if booleanValue {
666+
print("creating blocks")
667+
self.doSomething()
668+
} else {
669+
let _ = _move(self)
670+
print("creating blocks2")
671+
}
672+
673+
self = .init(k: Klass())
674+
print("foo bar")
675+
}
676+
}

0 commit comments

Comments
 (0)