Skip to content

Commit 344ef3d

Browse files
committed
[ClosureLifetimeFixup] Flag dead-end destroys.
1 parent 45f3cf9 commit 344ef3d

File tree

2 files changed

+62
-19
lines changed

2 files changed

+62
-19
lines changed

lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,22 @@
1414

1515
#include "swift/Basic/Assertions.h"
1616
#include "swift/Basic/Defer.h"
17+
#include "swift/SIL/BasicBlockDatastructures.h"
1718
#include "swift/SIL/DebugUtils.h"
1819
#include "swift/SIL/InstructionUtils.h"
1920
#include "swift/SIL/PrunedLiveness.h"
2021
#include "swift/SIL/SILArgument.h"
2122
#include "swift/SIL/SILBuilder.h"
2223
#include "swift/SIL/SILInstruction.h"
2324
#include "swift/SIL/SILValue.h"
24-
#include "swift/SIL/BasicBlockDatastructures.h"
2525
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
26+
#include "swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h"
2627
#include "swift/SILOptimizer/PassManager/Passes.h"
2728
#include "swift/SILOptimizer/PassManager/Transforms.h"
2829
#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h"
2930
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
30-
#include "swift/SILOptimizer/Utils/OwnershipOptUtils.h"
3131
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
32+
#include "swift/SILOptimizer/Utils/OwnershipOptUtils.h"
3233
#include "swift/SILOptimizer/Utils/SILSSAUpdater.h"
3334
#include "swift/SILOptimizer/Utils/StackNesting.h"
3435

@@ -445,14 +446,6 @@ static BuiltinInst *getEndAsyncLet(BuiltinInst *startAsyncLet) {
445446
/// a closure is used by \p closureUser.
446447
static void insertAfterClosureUser(SILInstruction *closureUser,
447448
function_ref<void(SILBuilder &)> insertFn) {
448-
// Don't insert any destroy or deallocation right before an unreachable.
449-
// It's not needed an will only add up to code size.
450-
auto insertAtNonUnreachable = [&](SILBuilder &builder) {
451-
if (isa<UnreachableInst>(builder.getInsertionPoint()))
452-
return;
453-
insertFn(builder);
454-
};
455-
456449
{
457450
SILInstruction *userForBorrow = closureUser;
458451
if (auto *m = dyn_cast<MoveOnlyWrapperToCopyableValueInst>(userForBorrow))
@@ -468,7 +461,7 @@ static void insertAfterClosureUser(SILInstruction *closureUser,
468461

469462
for (auto eb : endBorrows) {
470463
SILBuilderWithScope builder(std::next(eb->getIterator()));
471-
insertAtNonUnreachable(builder);
464+
insertFn(builder);
472465
}
473466
return;
474467
}
@@ -479,12 +472,12 @@ static void insertAfterClosureUser(SILInstruction *closureUser,
479472
if (!endAsyncLet)
480473
return;
481474
SILBuilderWithScope builder(std::next(endAsyncLet->getIterator()));
482-
insertAtNonUnreachable(builder);
475+
insertFn(builder);
483476
return;
484477
}
485478
FullApplySite fas = FullApplySite::isa(closureUser);
486479
assert(fas);
487-
fas.insertAfterApplication(insertAtNonUnreachable);
480+
fas.insertAfterApplication(insertFn);
488481
}
489482

490483
static SILValue skipConvert(SILValue v) {
@@ -1000,6 +993,7 @@ static SILValue tryRewriteToPartialApplyStack(
1000993

1001994
static bool tryExtendLifetimeToLastUse(
1002995
ConvertEscapeToNoEscapeInst *cvt, DominanceAnalysis *dominanceAnalysis,
996+
DeadEndBlocksAnalysis *deadEndBlocksAnalysis,
1003997
llvm::DenseMap<SILInstruction *, SILInstruction *> &memoized,
1004998
llvm::DenseSet<SILBasicBlock *> &unreachableBlocks,
1005999
InstructionDeleter &deleter, const bool &modifiedCFG) {
@@ -1048,10 +1042,22 @@ static bool tryExtendLifetimeToLastUse(
10481042
cvt->setLifetimeGuaranteed();
10491043
cvt->setOperand(closureCopy);
10501044

1051-
insertAfterClosureUser(singleUser, [closureCopy](SILBuilder &builder) {
1052-
auto loc = RegularLocation(builder.getInsertionPointLoc());
1053-
builder.createDestroyValue(loc, closureCopy);
1054-
});
1045+
auto *function = cvt->getFunction();
1046+
// The CFG may have been modified during this run, which would have made
1047+
// dead-end blocks analysis invalid. Mark it invalid it now if that
1048+
// happened. If the CFG hasn't been modified, this is a noop thanks to
1049+
// DeadEndBlocksAnalysis::shouldInvalidate.
1050+
deadEndBlocksAnalysis->invalidate(function,
1051+
analysisInvalidationKind(modifiedCFG));
1052+
auto *deadEndBlocks = deadEndBlocksAnalysis->get(function);
1053+
1054+
insertAfterClosureUser(
1055+
singleUser, [closureCopy, deadEndBlocks](SILBuilder &builder) {
1056+
auto loc = RegularLocation(builder.getInsertionPointLoc());
1057+
auto isDeadEnd = IsDeadEnd_t(
1058+
deadEndBlocks->isDeadEnd(builder.getInsertionPoint()->getParent()));
1059+
builder.createDestroyValue(loc, closureCopy, DontPoisonRefs, isDeadEnd);
1060+
});
10551061
/*
10561062
llvm::errs() << "after lifetime extension of\n";
10571063
escapingClosure->dump();
@@ -1440,6 +1446,7 @@ static void computeUnreachableBlocks(
14401446

14411447
static bool fixupClosureLifetimes(SILFunction &fn,
14421448
DominanceAnalysis *dominanceAnalysis,
1449+
DeadEndBlocksAnalysis *deadEndBlocksAnalysis,
14431450
bool &checkStackNesting, bool &modifiedCFG) {
14441451
bool changed = false;
14451452

@@ -1476,7 +1483,8 @@ static bool fixupClosureLifetimes(SILFunction &fn,
14761483
}
14771484
}
14781485

1479-
if (tryExtendLifetimeToLastUse(cvt, dominanceAnalysis, memoizedQueries,
1486+
if (tryExtendLifetimeToLastUse(cvt, dominanceAnalysis,
1487+
deadEndBlocksAnalysis, memoizedQueries,
14801488
unreachableBlocks, updater.getDeleter(),
14811489
/*const*/ modifiedCFG)) {
14821490
changed = true;
@@ -1516,9 +1524,11 @@ class ClosureLifetimeFixup : public SILFunctionTransform {
15161524
bool modifiedCFG = false;
15171525

15181526
auto *dominanceAnalysis = PM->getAnalysis<DominanceAnalysis>();
1527+
auto *deadEndBlocksAnalysis = getAnalysis<DeadEndBlocksAnalysis>();
15191528

15201529
if (fixupClosureLifetimes(*getFunction(), dominanceAnalysis,
1521-
checkStackNesting, modifiedCFG)) {
1530+
deadEndBlocksAnalysis, checkStackNesting,
1531+
modifiedCFG)) {
15221532
updateBorrowedFrom(getPassManager(), getFunction());
15231533
if (checkStackNesting){
15241534
modifiedCFG |=

test/SILOptimizer/closure-lifetime-fixup.sil

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ sil @noescapeBlock3 : $@convention(c) (Optional<@convention(block) @noescape (Op
1414
sil @$sSS10FoundationE19_bridgeToObjectiveCSo8FakeNSStringCyF : $@convention(method) (@guaranteed String) -> @owned FakeNSString
1515
sil @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String
1616
sil @$sSSSgIegg_So8FakeNSStringCSgIyBy_TR : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed (@guaranteed Optional<String>) -> (), Optional<FakeNSString>) -> ()
17+
sil @nothrower : $@convention(thin) () -> Int
18+
sil @rethrower : $@convention(thin) (@guaranteed @noescape @callee_guaranteed () -> (Int, @error any Error)) -> (Int, @error any Error)
1719

1820
// Just make sure that we perform the optimization and do not trigger the ownership verifier.
1921
//
@@ -377,3 +379,34 @@ bb2(%19 : @owned $any Error):
377379
dealloc_stack %5 : $*Int
378380
throw %19 : $any Error
379381
}
382+
383+
// The destroy that's created in the dead-end should be flagged [dead_end].
384+
// CHECK-LABEL: sil [ossa] @testRethrowingNonthrowing : {{.*}} {
385+
// CHECK: [[NOTHROWER:%[^,]+]] = function_ref @nothrower
386+
// CHECK: [[THICK_NOTHROWER:%[^,]+]] = thin_to_thick_function [[NOTHROWER]]
387+
// CHECK: [[THROWING_NOTHROWER:%[^,]+]] = convert_function [[THICK_NOTHROWER]]
388+
// CHECK: [[NOTHROWER_COPY:%[^,]+]] = copy_value [[THROWING_NOTHROWER]]
389+
// CHECK: {{bb[0-9]+}}
390+
// CHECK: destroy_value [[NOTHROWER_COPY]]
391+
// CHECK: {{bb[0-9]+}}
392+
// CHECK: destroy_value [dead_end] [[NOTHROWER_COPY]]
393+
// CHECK-LABEL: } // end sil function 'testRethrowingNonthrowing'
394+
sil [ossa] @testRethrowingNonthrowing : $@convention(thin) () -> () {
395+
bb0:
396+
%nothrower = function_ref @nothrower : $@convention(thin) () -> Int
397+
%thick_nothrower = thin_to_thick_function %nothrower : $@convention(thin) () -> Int to $@callee_guaranteed () -> Int
398+
%throwing_nothrower = convert_function %thick_nothrower : $@callee_guaranteed () -> Int to $@callee_guaranteed () -> (Int, @error any Error)
399+
%nonescaping_nothrower = convert_escape_to_noescape [not_guaranteed] %throwing_nothrower : $@callee_guaranteed () -> (Int, @error any Error) to $@noescape @callee_guaranteed () -> (Int, @error any Error)
400+
%rethrower = function_ref @rethrower : $@convention(thin) (@guaranteed @noescape @callee_guaranteed () -> (Int, @error any Error)) -> (Int, @error any Error)
401+
try_apply %rethrower(%nonescaping_nothrower) : $@convention(thin) (@guaranteed @noescape @callee_guaranteed () -> (Int, @error any Error)) -> (Int, @error any Error), normal bb1, error bb2
402+
403+
bb1(%val : $Int):
404+
destroy_value %nonescaping_nothrower : $@noescape @callee_guaranteed () -> (Int, @error any Error)
405+
%8 = tuple ()
406+
return %8 : $()
407+
408+
bb2(%error : @owned $any Error):
409+
destroy_value [dead_end] %error : $any Error
410+
destroy_value [dead_end] %nonescaping_nothrower : $@noescape @callee_guaranteed () -> (Int, @error any Error)
411+
unreachable
412+
}

0 commit comments

Comments
 (0)