Skip to content

Commit 3c0d8a6

Browse files
committed
ClosureLifetimeFixup: fix a crash due to an iterator invalidation problem.
The fix is the use the InstructionDeleter to iterate over (the not yet deleted) instructions in a block. rdar://80093482
1 parent 2385a97 commit 3c0d8a6

File tree

2 files changed

+46
-18
lines changed

2 files changed

+46
-18
lines changed

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
}

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)