Skip to content

Commit 9ad44a5

Browse files
committed
[ClosureLifetimeFixup] Dealloc args on frontier.
Previously, the dealloc_stacks created for the alloc_stacks used to pass @in_guaranteed arguments to on_stack closures were created after the users of the closure. When SILGen created these alloc_stacks in the same block as the users, this happened to work. Now that AddressLowering creates such alloc_stacks elsewhere, this approach results in invalid SIL. Here, the dealloc_stacks are instead at the end of each block in the dominance frontier of the alloc_stack.
1 parent 0d666e2 commit 9ad44a5

File tree

5 files changed

+103
-40
lines changed

5 files changed

+103
-40
lines changed

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -271,18 +271,14 @@ void releasePartialApplyCapturedArg(
271271
SILParameterInfo paramInfo,
272272
InstModCallbacks callbacks = InstModCallbacks());
273273

274-
void deallocPartialApplyCapturedArg(
275-
SILBuilder &builder, SILLocation loc, SILValue arg,
276-
SILParameterInfo paramInfo);
277-
278274
/// Insert destroys of captured arguments of partial_apply [stack].
279275
void insertDestroyOfCapturedArguments(
280276
PartialApplyInst *pai, SILBuilder &builder,
281277
llvm::function_ref<bool(SILValue)> shouldInsertDestroy =
282278
[](SILValue arg) -> bool { return true; });
283279

284-
void insertDeallocOfCapturedArguments(
285-
PartialApplyInst *pai, SILBuilder &builder);
280+
void insertDeallocOfCapturedArguments(PartialApplyInst *pai,
281+
DominanceInfo *domInfo);
286282

287283
/// This iterator 'looks through' one level of builtin expect users exposing all
288284
/// users of the looked through builtin expect instruction i.e it presents a

lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,12 @@ static SILValue skipConvert(SILValue v) {
444444
return pa;
445445
}
446446

447+
static SILAnalysis::InvalidationKind
448+
analysisInvalidationKind(const bool &modifiedCFG) {
449+
return modifiedCFG ? SILAnalysis::InvalidationKind::FunctionBody
450+
: SILAnalysis::InvalidationKind::CallsAndInstructions;
451+
}
452+
447453
/// Rewrite a partial_apply convert_escape_to_noescape sequence with a single
448454
/// apply/try_apply user to a partial_apply [stack] terminated with a
449455
/// dealloc_stack placed after the apply.
@@ -468,9 +474,10 @@ static SILValue skipConvert(SILValue v) {
468474
/// caller needs to use the StackNesting utility to update the dealloc_stack
469475
/// nesting.
470476
static SILValue tryRewriteToPartialApplyStack(
471-
ConvertEscapeToNoEscapeInst *cvt,
472-
SILInstruction *closureUser, InstructionDeleter &deleter,
473-
llvm::DenseMap<SILInstruction *, SILInstruction *> &memoized) {
477+
ConvertEscapeToNoEscapeInst *cvt, SILInstruction *closureUser,
478+
DominanceAnalysis *dominanceAnalysis, InstructionDeleter &deleter,
479+
llvm::DenseMap<SILInstruction *, SILInstruction *> &memoized,
480+
const bool &modifiedCFG) {
474481

475482
auto *origPA = dyn_cast<PartialApplyInst>(skipConvert(cvt->getOperand()));
476483
if (!origPA)
@@ -567,16 +574,24 @@ static SILValue tryRewriteToPartialApplyStack(
567574
auto loc = RegularLocation(builder.getInsertionPointLoc());
568575
builder.createDeallocStack(loc, newPA);
569576
insertDestroyOfCapturedArguments(newPA, builder);
570-
// dealloc_stack of the in_guaranteed capture is inserted
571-
insertDeallocOfCapturedArguments(newPA, builder);
572577
});
578+
// The CFG may have been modified during this run. If it was, the dominance
579+
// analysis would no longer be valid. Invalidate it now if necessary,
580+
// according to the kinds of changes that may have been made. Note that if
581+
// the CFG hasn't been modified, this is a noop thanks to
582+
// DominanceAnalysis::shouldInvalidate's definition.
583+
dominanceAnalysis->invalidate(closureUser->getFunction(),
584+
analysisInvalidationKind(modifiedCFG));
585+
// Insert dealloc_stacks of any in_guaranteed captures.
586+
insertDeallocOfCapturedArguments(
587+
newPA, dominanceAnalysis->get(closureUser->getFunction()));
573588
return closure;
574589
}
575590

576591
static bool tryExtendLifetimeToLastUse(
577-
ConvertEscapeToNoEscapeInst *cvt,
592+
ConvertEscapeToNoEscapeInst *cvt, DominanceAnalysis *dominanceAnalysis,
578593
llvm::DenseMap<SILInstruction *, SILInstruction *> &memoized,
579-
InstructionDeleter &deleter) {
594+
InstructionDeleter &deleter, const bool &modifiedCFG) {
580595
// If there is a single user that is an apply this is simple: extend the
581596
// lifetime of the operand until after the apply.
582597
auto *singleUser = lookThroughRebastractionUsers(cvt, memoized);
@@ -597,8 +612,9 @@ static bool tryExtendLifetimeToLastUse(
597612
return false;
598613
}
599614

600-
if (SILValue closure = tryRewriteToPartialApplyStack(cvt, singleUser,
601-
deleter, memoized)) {
615+
if (SILValue closure = tryRewriteToPartialApplyStack(
616+
cvt, singleUser, dominanceAnalysis, deleter, memoized,
617+
/*const*/ modifiedCFG)) {
602618
if (auto *cfi = dyn_cast<ConvertFunctionInst>(closure))
603619
closure = cfi->getOperand();
604620
if (endAsyncLet && isa<MarkDependenceInst>(closure)) {
@@ -993,8 +1009,9 @@ static bool fixupCopyBlockWithoutEscaping(CopyBlockWithoutEscapingInst *cb,
9931009
return true;
9941010
}
9951011

996-
static bool fixupClosureLifetimes(SILFunction &fn, bool &checkStackNesting,
997-
bool &modifiedCFG) {
1012+
static bool fixupClosureLifetimes(SILFunction &fn,
1013+
DominanceAnalysis *dominanceAnalysis,
1014+
bool &checkStackNesting, bool &modifiedCFG) {
9981015
bool changed = false;
9991016

10001017
// tryExtendLifetimeToLastUse uses a cache of recursive instruction use
@@ -1027,7 +1044,9 @@ static bool fixupClosureLifetimes(SILFunction &fn, bool &checkStackNesting,
10271044
}
10281045
}
10291046

1030-
if (tryExtendLifetimeToLastUse(cvt, memoizedQueries, updater.getDeleter())) {
1047+
if (tryExtendLifetimeToLastUse(cvt, dominanceAnalysis, memoizedQueries,
1048+
updater.getDeleter(),
1049+
/*const*/ modifiedCFG)) {
10311050
changed = true;
10321051
checkStackNesting = true;
10331052
continue;
@@ -1064,15 +1083,15 @@ class ClosureLifetimeFixup : public SILFunctionTransform {
10641083
bool checkStackNesting = false;
10651084
bool modifiedCFG = false;
10661085

1067-
if (fixupClosureLifetimes(*getFunction(), checkStackNesting, modifiedCFG)) {
1086+
auto *dominanceAnalysis = PM->getAnalysis<DominanceAnalysis>();
1087+
1088+
if (fixupClosureLifetimes(*getFunction(), dominanceAnalysis,
1089+
checkStackNesting, modifiedCFG)) {
10681090
if (checkStackNesting){
10691091
modifiedCFG |=
10701092
StackNesting::fixNesting(getFunction()) == StackNesting::Changes::CFG;
10711093
}
1072-
if (modifiedCFG)
1073-
invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody);
1074-
else
1075-
invalidateAnalysis(SILAnalysis::InvalidationKind::CallsAndInstructions);
1094+
invalidateAnalysis(analysisInvalidationKind(modifiedCFG));
10761095
}
10771096
LLVM_DEBUG(getFunction()->verify());
10781097

lib/SILOptimizer/Utils/InstOptUtils.cpp

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -910,15 +910,6 @@ void swift::releasePartialApplyCapturedArg(SILBuilder &builder, SILLocation loc,
910910
emitDestroyOperation(builder, loc, arg, callbacks);
911911
}
912912

913-
void swift::deallocPartialApplyCapturedArg(SILBuilder &builder, SILLocation loc,
914-
SILValue arg,
915-
SILParameterInfo paramInfo) {
916-
if (!paramInfo.isIndirectInGuaranteed())
917-
return;
918-
919-
builder.createDeallocStack(loc, arg);
920-
}
921-
922913
static bool
923914
deadMarkDependenceUser(SILInstruction *inst,
924915
SmallVectorImpl<SILInstruction *> &deleteInsts) {
@@ -1473,19 +1464,32 @@ void swift::insertDestroyOfCapturedArguments(
14731464
}
14741465
}
14751466

1476-
void swift::insertDeallocOfCapturedArguments(
1477-
PartialApplyInst *pai, SILBuilder &builder) {
1467+
void swift::insertDeallocOfCapturedArguments(PartialApplyInst *pai,
1468+
DominanceInfo *domInfo) {
14781469
assert(pai->isOnStack());
14791470

14801471
ApplySite site(pai);
14811472
SILFunctionConventions calleeConv(site.getSubstCalleeType(),
14821473
pai->getModule());
1483-
auto loc = CleanupLocation(RegularLocation::getAutoGeneratedLocation());
14841474
for (auto &arg : pai->getArgumentOperands()) {
14851475
unsigned calleeArgumentIndex = site.getCalleeArgIndex(arg);
14861476
assert(calleeArgumentIndex >= calleeConv.getSILArgIndexOfFirstParam());
14871477
auto paramInfo = calleeConv.getParamInfoForSILArg(calleeArgumentIndex);
1488-
deallocPartialApplyCapturedArg(builder, loc, arg.get(), paramInfo);
1478+
if (!paramInfo.isIndirectInGuaranteed())
1479+
continue;
1480+
1481+
SmallVector<SILBasicBlock *, 4> boundary;
1482+
auto *asi = cast<AllocStackInst>(arg.get());
1483+
computeDominatedBoundaryBlocks(asi->getParent(), domInfo, boundary);
1484+
1485+
for (auto *block : boundary) {
1486+
auto *terminator = block->getTerminator();
1487+
auto builder = SILBuilder(terminator);
1488+
if (isa<UnreachableInst>(terminator))
1489+
continue;
1490+
builder.createDeallocStack(CleanupLocation(terminator->getLoc()),
1491+
arg.get());
1492+
}
14891493
}
14901494
}
14911495

test/SILOptimizer/closure-lifetime-fixup-debuginfo.sil

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ associatedtype Index
1818
// CHECK: [[CAPTURED_ADDR:%[^,]+]] = alloc_stack $Self
1919
// CHECK: try_apply {{.*}}, normal [[SUCCESS:bb[0-9]+]]
2020
// CHECK: [[SUCCESS]]
21-
// CHECK: destroy_addr [[CAPTURED_ADDR]]{{.*}} // id: {{%[^,]+}}; <invalid loc>:cleanup:auto_gen
22-
// ^^^^^^^
23-
// CHECK: dealloc_stack [[CAPTURED_ADDR]]{{.*}} // id: {{%[^,]+}}; <invalid loc>:cleanup:auto_gen
24-
// ^^^^^^^
21+
// CHECK: destroy_addr [[CAPTURED_ADDR]]{{.*}}:cleanup:
22+
// ^^^^^^^
23+
// CHECK: dealloc_stack [[CAPTURED_ADDR]]{{.*}}:cleanup:
24+
// ^^^^^^^
2525
// CHECK-LABEL: } // end sil function 'destroy_and_dealloc_of_capture_are_cleanups'
2626
sil [ossa] @destroy_and_dealloc_of_capture_are_cleanups : $@convention(method) <Self where Self : Indexable> (@in Self) -> () {
2727
bb0(%self : $*Self):

test/SILOptimizer/closure-lifetime-fixup.sil

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,3 +256,47 @@ bb3:
256256
return %25 : $()
257257
}
258258

259+
// CHECK-LABEL: sil [ossa] @test_alloc_stack_arg_with_frontier_outside_try_apply_successors : $@convention(thin) <Self> (@in_guaranteed Self) -> @error any Error {
260+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] :
261+
// CHECK: [[STACK:%[^,]+]] = alloc_stack $Self
262+
// CHECK: try_apply undef() {{.*}}, normal [[SUCCESS_1:bb[0-9]+]], error [[FAILURE_1:bb[0-9]+]]
263+
// CHECK: [[SUCCESS_1]]
264+
// CHECK: copy_addr [[INSTANCE]] to [init] [[STACK]] : $*Self
265+
// CHECK: [[CLOSURE:%[^,]+]] = partial_apply [callee_guaranteed] [on_stack] undef<Self>([[STACK]])
266+
// CHECK: [[DEPENDENCY:%[^,]+]] = mark_dependence [[CLOSURE]] {{.*}} on [[STACK]]
267+
// CHECK: try_apply undef([[DEPENDENCY]]) {{.*}}, normal [[SUCCESS_2:bb[0-9]+]], error [[FAILURE_2:bb[0-9]+]]
268+
// CHECK: [[SUCCESS_2]]
269+
// CHECK: dealloc_stack [[CLOSURE]]
270+
// CHECK: destroy_addr [[STACK]]
271+
// CHECK: dealloc_stack [[STACK]] : $*Self
272+
// CHECK: [[FAILURE_1]]
273+
// CHECK: dealloc_stack [[STACK]]
274+
// CHECK: throw
275+
// CHECK: [[FAILURE_2]]
276+
// CHECK-NOT: dealloc_stack
277+
// CHECK: unreachable
278+
// CHECK-LABEL: } // end sil function 'test_alloc_stack_arg_with_frontier_outside_try_apply_successors'
279+
sil [ossa] @test_alloc_stack_arg_with_frontier_outside_try_apply_successors : $@convention(thin) <Self> (@in_guaranteed Self) -> (@error any Error) {
280+
bb0(%2 : $*Self):
281+
%4 = alloc_stack $Self
282+
try_apply undef() : $@convention(thin) () -> (@error any Error), normal bb1, error bb3
283+
284+
bb1(%10 : $()):
285+
copy_addr %2 to [init] %4 : $*Self
286+
%13 = partial_apply [callee_guaranteed] undef<Self>(%4) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> ()
287+
%15 = convert_escape_to_noescape [not_guaranteed] %13 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
288+
try_apply undef(%15) : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> (@error any Error), normal bb2, error bb4
289+
290+
bb2(%18 : $()):
291+
destroy_value %13 : $@callee_guaranteed () -> ()
292+
%21 = tuple ()
293+
dealloc_stack %4 : $*Self
294+
return %21 : $()
295+
296+
bb3(%25 : @owned $any Error):
297+
dealloc_stack %4 : $*Self
298+
throw %25 : $any Error
299+
300+
bb4(%29 : @owned $any Error):
301+
unreachable
302+
}

0 commit comments

Comments
 (0)