Skip to content

Commit 1c71091

Browse files
authored
Merge pull request #38308 from eeckstein/fix-closure-lifetime-fixup
ClosureLifetimeFixup: fix a crash due to an iterator invalidation problem.
2 parents 9d3cd5e + 3c0d8a6 commit 1c71091

File tree

7 files changed

+67
-43
lines changed

7 files changed

+67
-43
lines changed

include/swift/SILOptimizer/Utils/CFGOptUtils.h

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,21 +47,7 @@ struct InstModCallbacks;
4747
/// \return The created branch. The old branch is deleted.
4848
/// The argument is appended at the end of the argument tuple.
4949
TermInst *addNewEdgeValueToBranch(TermInst *branch, SILBasicBlock *dest,
50-
SILValue val, InstModCallbacks &callbacks);
51-
52-
/// Adds a new argument to an edge between a branch and a destination
53-
/// block.
54-
///
55-
/// \param branch The terminator to add the argument to.
56-
/// \param dest The destination block of the edge.
57-
/// \param val The value to the arguments of the branch.
58-
/// \return The created branch. The old branch is deleted.
59-
/// The argument is appended at the end of the argument tuple.
60-
inline TermInst *addNewEdgeValueToBranch(TermInst *branch, SILBasicBlock *dest,
61-
SILValue val) {
62-
InstModCallbacks callbacks;
63-
return addNewEdgeValueToBranch(branch, dest, val, callbacks);
64-
}
50+
SILValue val, InstructionDeleter &deleter);
6551

6652
/// Changes the edge value between a branch and destination basic block
6753
/// at the specified index. Changes all edges from \p Branch to \p Dest to carry

include/swift/SILOptimizer/Utils/SILSSAUpdater.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define SWIFT_SIL_SILSSAUPDATER_H
1515

1616
#include "llvm/Support/Allocator.h"
17+
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
1718
#include "swift/SIL/SILInstruction.h"
1819
#include "swift/SIL/SILValue.h"
1920

@@ -58,6 +59,10 @@ class SILSSAUpdater {
5859
// If not null updated with inserted 'phi' nodes (SILArgument).
5960
SmallVectorImpl<SILPhiArgument *> *insertedPhis;
6061

62+
// Used to delete branch instructions when they are replaced for adding
63+
// phi arguments.
64+
InstructionDeleter deleter;
65+
6166
// Not copyable.
6267
void operator=(const SILSSAUpdater &) = delete;
6368
SILSSAUpdater(const SILSSAUpdater &) = delete;
@@ -67,6 +72,8 @@ class SILSSAUpdater {
6772
SmallVectorImpl<SILPhiArgument *> *insertedPhis = nullptr);
6873
~SILSSAUpdater();
6974

75+
InstructionDeleter &getDeleter() { return deleter; }
76+
7077
void setInsertedPhis(SmallVectorImpl<SILPhiArgument *> *inputInsertedPhis) {
7178
insertedPhis = inputInsertedPhis;
7279
}

lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,8 @@ cleanupDeadTrivialPhiArgs(SILValue initialValue,
208208
/// that case where we have to consider that destroy_value, we have a simpler
209209
/// time here.
210210
static void extendLifetimeToEndOfFunction(SILFunction &fn,
211-
ConvertEscapeToNoEscapeInst *cvt) {
211+
ConvertEscapeToNoEscapeInst *cvt,
212+
SILSSAUpdater &updater) {
212213
auto escapingClosure = cvt->getOperand();
213214
auto escapingClosureTy = escapingClosure->getType();
214215
auto optionalEscapingClosureTy = SILType::getOptionalType(escapingClosureTy);
@@ -239,7 +240,7 @@ static void extendLifetimeToEndOfFunction(SILFunction &fn,
239240
// use SILSSAUpdater::GetValueInMiddleOfBlock() to extend the object's
240241
// lifetime respecting loops.
241242
SmallVector<SILPhiArgument *, 8> insertedPhis;
242-
SILSSAUpdater updater(&insertedPhis);
243+
updater.setInsertedPhis(&insertedPhis);
243244
updater.initialize(optionalEscapingClosureTy, fn.hasOwnership()
244245
? OwnershipKind::Owned
245246
: OwnershipKind::None);
@@ -443,7 +444,7 @@ static SILValue skipConvert(SILValue v) {
443444
/// nesting.
444445
static SILValue tryRewriteToPartialApplyStack(
445446
ConvertEscapeToNoEscapeInst *cvt,
446-
SILInstruction *closureUser, SILBasicBlock::iterator &advanceIfDelete,
447+
SILInstruction *closureUser, InstructionDeleter &deleter,
447448
llvm::DenseMap<SILInstruction *, SILInstruction *> &memoized) {
448449

449450
auto *origPA = dyn_cast<PartialApplyInst>(skipConvert(cvt->getOperand()));
@@ -457,10 +458,8 @@ static SILValue tryRewriteToPartialApplyStack(
457458
// Whenever we delete an instruction advance the iterator and remove the
458459
// instruction from the memoized map.
459460
auto saveDeleteInst = [&](SILInstruction *i) {
460-
if (&*advanceIfDelete == i)
461-
++advanceIfDelete;
462461
memoized.erase(i);
463-
i->eraseFromParent();
462+
deleter.forceDelete(i);
464463
};
465464

466465
// Look for a single non ref count user of the partial_apply.
@@ -534,7 +533,7 @@ static SILValue tryRewriteToPartialApplyStack(
534533
SmallVector<Operand*, 16> Uses(arg.get()->getUses());
535534
for (auto use : Uses)
536535
if (auto *deallocInst = dyn_cast<DeallocStackInst>(use->getUser()))
537-
deallocInst->eraseFromParent();
536+
deleter.forceDelete(deallocInst);
538537
}
539538
}
540539

@@ -552,7 +551,7 @@ static SILValue tryRewriteToPartialApplyStack(
552551
static bool tryExtendLifetimeToLastUse(
553552
ConvertEscapeToNoEscapeInst *cvt,
554553
llvm::DenseMap<SILInstruction *, SILInstruction *> &memoized,
555-
SILBasicBlock::iterator &advanceIfDelete) {
554+
InstructionDeleter &deleter) {
556555
// If there is a single user that is an apply this is simple: extend the
557556
// lifetime of the operand until after the apply.
558557
auto *singleUser = lookThroughRebastractionUsers(cvt, memoized);
@@ -574,7 +573,7 @@ static bool tryExtendLifetimeToLastUse(
574573
}
575574

576575
if (SILValue closure = tryRewriteToPartialApplyStack(cvt, singleUser,
577-
advanceIfDelete, memoized)) {
576+
deleter, memoized)) {
578577
if (auto *cfi = dyn_cast<ConvertFunctionInst>(closure))
579578
closure = cfi->getOperand();
580579
if (endAsyncLet && isa<MarkDependenceInst>(closure)) {
@@ -587,7 +586,7 @@ static bool tryExtendLifetimeToLastUse(
587586
builder.createBuiltin(endAsyncLet->getLoc(), endAsyncLet->getName(),
588587
endAsyncLet->getType(), endAsyncLet->getSubstitutions(),
589588
{endAsyncLet->getOperand(0), closure});
590-
endAsyncLet->eraseFromParent();
589+
deleter.forceDelete(endAsyncLet);
591590
}
592591
return true;
593592
}
@@ -797,6 +796,7 @@ static SILInstruction *getOnlyDestroy(CopyBlockWithoutEscapingInst *cb) {
797796
/// cond_fail %e
798797
/// destroy_value %closure
799798
static bool fixupCopyBlockWithoutEscaping(CopyBlockWithoutEscapingInst *cb,
799+
InstructionDeleter &deleter,
800800
bool &modifiedCFG) {
801801
// Find the end of the lifetime of the copy_block_without_escaping
802802
// instruction.
@@ -837,7 +837,7 @@ static bool fixupCopyBlockWithoutEscaping(CopyBlockWithoutEscapingInst *cb,
837837
SILBuilderWithScope b(cb);
838838
auto *newCB = b.createCopyBlock(loc, cb->getBlock());
839839
cb->replaceAllUsesWith(newCB);
840-
cb->eraseFromParent();
840+
deleter.forceDelete(cb);
841841

842842
auto autoGenLoc = RegularLocation::getAutoGeneratedLocation();
843843

@@ -977,14 +977,12 @@ static bool fixupClosureLifetimes(SILFunction &fn, bool &checkStackNesting,
977977
llvm::DenseMap<SILInstruction *, SILInstruction *> memoizedQueries;
978978

979979
for (auto &block : fn) {
980-
auto i = block.begin();
981-
while (i != block.end()) {
982-
SILInstruction *inst = &*i;
983-
++i;
980+
SILSSAUpdater updater;
981+
for (SILInstruction *inst : updater.getDeleter().updatingRange(&block)) {
984982

985983
// Handle, copy_block_without_escaping instructions.
986984
if (auto *cb = dyn_cast<CopyBlockWithoutEscapingInst>(inst)) {
987-
if (fixupCopyBlockWithoutEscaping(cb, modifiedCFG)) {
985+
if (fixupCopyBlockWithoutEscaping(cb, updater.getDeleter(), modifiedCFG)) {
988986
changed = true;
989987
}
990988
continue;
@@ -1004,15 +1002,15 @@ static bool fixupClosureLifetimes(SILFunction &fn, bool &checkStackNesting,
10041002
}
10051003
}
10061004

1007-
if (tryExtendLifetimeToLastUse(cvt, memoizedQueries, i)) {
1005+
if (tryExtendLifetimeToLastUse(cvt, memoizedQueries, updater.getDeleter())) {
10081006
changed = true;
10091007
checkStackNesting = true;
10101008
continue;
10111009
}
10121010

10131011
// Otherwise, extend the lifetime of the operand to the end of the
10141012
// function.
1015-
extendLifetimeToEndOfFunction(fn, cvt);
1013+
extendLifetimeToEndOfFunction(fn, cvt, updater);
10161014
changed = true;
10171015
}
10181016
}

lib/SILOptimizer/Utils/CFGOptUtils.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ using namespace swift;
2626

2727
TermInst *swift::addNewEdgeValueToBranch(TermInst *branch, SILBasicBlock *dest,
2828
SILValue val,
29-
InstModCallbacks &callbacks) {
29+
InstructionDeleter &deleter) {
3030
SILBuilderWithScope builder(branch);
3131
TermInst *newBr = nullptr;
3232

@@ -53,7 +53,7 @@ TermInst *swift::addNewEdgeValueToBranch(TermInst *branch, SILBasicBlock *dest,
5353
cbi->getLoc(), cbi->getCondition(), cbi->getTrueBB(), trueArgs,
5454
cbi->getFalseBB(), falseArgs, cbi->getTrueBBCount(),
5555
cbi->getFalseBBCount());
56-
callbacks.createdNewInst(newBr);
56+
deleter.getCallbacks().createdNewInst(newBr);
5757
} else if (auto *bi = dyn_cast<BranchInst>(branch)) {
5858
SmallVector<SILValue, 8> args;
5959

@@ -63,13 +63,13 @@ TermInst *swift::addNewEdgeValueToBranch(TermInst *branch, SILBasicBlock *dest,
6363
args.push_back(val);
6464
assert(args.size() == dest->getNumArguments());
6565
newBr = builder.createBranch(bi->getLoc(), bi->getDestBB(), args);
66-
callbacks.createdNewInst(newBr);
66+
deleter.getCallbacks().createdNewInst(newBr);
6767
} else {
6868
// At the moment we can only add arguments to br and cond_br.
6969
llvm_unreachable("Can't add argument to terminator");
7070
}
7171

72-
callbacks.deleteInst(branch);
72+
deleter.forceDelete(branch);
7373

7474
return newBr;
7575
}

lib/SILOptimizer/Utils/OwnershipOptUtils.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ insertOwnedBaseValueAlongBranchEdge(BranchInst *bi, SILValue innerCopy,
7878
// argument.
7979
auto *phiArg =
8080
destBB->createPhiArgument(innerCopy->getType(), OwnershipKind::Owned);
81-
addNewEdgeValueToBranch(bi, destBB, innerCopy, callbacks);
81+
InstructionDeleter deleter(callbacks);
82+
addNewEdgeValueToBranch(bi, destBB, innerCopy, deleter);
8283

8384
// Grab our predecessor blocks, ignoring us, add to the branch edge an
8485
// undef corresponding to our value.
@@ -93,7 +94,7 @@ insertOwnedBaseValueAlongBranchEdge(BranchInst *bi, SILValue innerCopy,
9394
continue;
9495
addNewEdgeValueToBranch(
9596
predBlock->getTerminator(), destBB,
96-
SILUndef::get(innerCopy->getType(), *destBB->getParent()), callbacks);
97+
SILUndef::get(innerCopy->getType(), *destBB->getParent()), deleter);
9798
}
9899

99100
return phiArg;

lib/SILOptimizer/Utils/SILSSAUpdater.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,9 +230,10 @@ SILValue SILSSAUpdater::getValueInMiddleOfBlock(SILBasicBlock *block) {
230230

231231
// Create a new phi node.
232232
SILPhiArgument *phiArg = block->createPhiArgument(type, OwnershipKind::Owned);
233-
for (auto &pair : predVals)
234-
addNewEdgeValueToBranch(pair.first->getTerminator(), block, pair.second);
235-
233+
for (auto &pair : predVals) {
234+
addNewEdgeValueToBranch(pair.first->getTerminator(), block, pair.second,
235+
deleter);
236+
}
236237
if (insertedPhis)
237238
insertedPhis->push_back(phiArg);
238239

@@ -326,7 +327,8 @@ class SSAUpdaterTraits<SILSSAUpdater> {
326327

327328
for (auto *predBlock : predBlockList) {
328329
TermInst *ti = predBlock->getTerminator();
329-
addNewEdgeValueToBranch(ti, block, ssaUpdater->phiSentinel.get());
330+
addNewEdgeValueToBranch(ti, block, ssaUpdater->phiSentinel.get(),
331+
ssaUpdater->deleter);
330332
}
331333

332334
return phi;

test/SILOptimizer/closure-lifetime-fixup.sil

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,33 @@ bb0(%0 : @guaranteed $Klass, %1 : @guaranteed $Klass):
178178
%42 = tuple ()
179179
return %42 : $()
180180
}
181+
182+
sil @simpleClosure : $@convention(thin) () -> ()
183+
184+
// Don't crash.
185+
// CHECK-LABEL: sil [ossa] @testIteratorInvalidation
186+
// CHECK: [[C:%.*]] = thin_to_thick_function
187+
// CHECK: [[CC:%.*]] = copy_value [[C]]
188+
// CHECK: [[NE:%.*]] = convert_escape_to_noescape [[CC]]
189+
// CHECK: br bb3([[NE:%.*]]
190+
// CHECK: } // end sil function 'testIteratorInvalidation'
191+
sil [ossa] @testIteratorInvalidation : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> () {
192+
bb0(%0 : $@noescape @callee_guaranteed () -> ()):
193+
%2 = function_ref @simpleClosure : $@convention(thin) () -> ()
194+
%3 = thin_to_thick_function %2 : $@convention(thin) () -> () to $@callee_guaranteed () -> ()
195+
cond_br undef, bb1, bb2
196+
197+
bb1:
198+
br bb3(%0 : $@noescape @callee_guaranteed () -> ())
199+
200+
bb2:
201+
%11 = convert_escape_to_noescape [not_guaranteed] %3 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
202+
br bb3(%11 : $@noescape @callee_guaranteed () -> ())
203+
204+
205+
bb3(%13 : $@noescape @callee_guaranteed () -> ()):
206+
%15 = apply %13() : $@noescape @callee_guaranteed () -> ()
207+
%16 = tuple ()
208+
return %16 : $()
209+
}
210+

0 commit comments

Comments
 (0)