Skip to content

Commit 1b3fa26

Browse files
Merge pull request #39883 from nate-chandler/lexical_lifetimes/avoid-incorrect-elimination
[SILOptimizer] Keep lexical lifetime markers.
2 parents ea4f20a + 3bb1766 commit 1b3fa26

File tree

4 files changed

+77
-0
lines changed

4 files changed

+77
-0
lines changed

lib/SILOptimizer/SemanticARC/BorrowScopeOpts.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ bool SemanticARCOptVisitor::visitBeginBorrowInst(BeginBorrowInst *bbi) {
2929
if (!ctx.shouldPerform(ARCTransformKind::RedundantBorrowScopeElimPeephole))
3030
return false;
3131

32+
// Lexical borrow scopes must remain in order to ensure that value lifetimes
33+
// are not observably shortened.
34+
if (bbi->isLexical())
35+
return false;
36+
3237
auto kind = bbi->getOperand().getOwnershipKind();
3338
SmallVector<EndBorrowInst *, 16> endBorrows;
3439
for (auto *op : bbi->getUses()) {

lib/SILOptimizer/Transforms/TempRValueElimination.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,12 @@ void TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
495495
if (!tempObj)
496496
return;
497497

498+
// If the storage corresponds to a source-level var, do not optimize here.
499+
// Mem2Reg will transform the lexical alloc_stack into a lexical begin_borrow
500+
// which will ensure that the value's lifetime isn't observably shortened.
501+
if (tempObj->isLexical())
502+
return;
503+
498504
bool isOSSA = copyInst->getFunction()->hasOwnership();
499505

500506
SILValue copySrc = copyInst->getSrc();
@@ -633,6 +639,13 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
633639
return std::next(si->getIterator());
634640
}
635641

642+
// If the storage corresponds to a source-level var, do not optimize here.
643+
// Mem2Reg will transform the lexical alloc_stack into a lexical begin_borrow
644+
// which will ensure that the value's lifetime isn't observably shortened.
645+
if (tempObj->isLexical()) {
646+
return std::next(si->getIterator());
647+
}
648+
636649
// If our tempObj has a dynamic lifetime (meaning it is conditionally
637650
// initialized, conditionally taken, etc), we can not convert its uses to SSA
638651
// while eliminating it simply. So bail.

lib/SILOptimizer/Utils/CanonicalizeInstruction.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,11 @@ static SILBasicBlock::iterator
450450
eliminateSimpleBorrows(BeginBorrowInst *bbi, CanonicalizeInstruction &pass) {
451451
auto next = std::next(bbi->getIterator());
452452

453+
// Never eliminate lexical borrow scopes. They must be kept to ensure that
454+
// value lifetimes aren't observably shortened.
455+
if (bbi->isLexical())
456+
return next;
457+
453458
// We know that our borrow is completely within the lifetime of its base value
454459
// if the borrow is never reborrowed. We check for reborrows and do not
455460
// optimize such cases. Otherwise, we can eliminate our borrow and instead use
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: %target-run-simple-swift -Xfrontend -enable-experimental-lexical-lifetimes -O -Xfrontend -enable-copy-propagation | %FileCheck %s
2+
3+
// =============================================================================
4+
// = Declarations {{ =
5+
// =============================================================================
6+
7+
class C {
8+
init(_ d: D) {
9+
self.d = d
10+
}
11+
weak var d: D?
12+
func foo(_ string: String) {
13+
d?.cWillFoo(self, string)
14+
}
15+
}
16+
class D {
17+
func cWillFoo(_ c: C, _ string: String) {
18+
print(#function, string)
19+
}
20+
}
21+
22+
// =============================================================================
23+
// = Declarations }} =
24+
// =============================================================================
25+
26+
// =============================================================================
27+
// = Tests {{ =
28+
// =============================================================================
29+
30+
func test_localLetKeepsObjectAliveBeyondCallToClassWithWeakReference() {
31+
let d = D()
32+
let c = C(d)
33+
// CHECK: cWillFoo{{.*}} test_localLetKeepsObjectAliveBeyondCallToClassWithWeakReference
34+
c.foo(#function)
35+
}
36+
37+
func test_localVarKeepsObjectAliveBeyondCallToClassWithWeakReference() {
38+
var d = D()
39+
let c = C(d)
40+
// CHECK: cWillFoo{{.*}} test_localVarKeepsObjectAliveBeyondCallToClassWithWeakReference
41+
c.foo(#function)
42+
}
43+
44+
// =============================================================================
45+
// = Tests }} =
46+
// =============================================================================
47+
48+
func run() {
49+
test_localLetKeepsObjectAliveBeyondCallToClassWithWeakReference()
50+
test_localVarKeepsObjectAliveBeyondCallToClassWithWeakReference()
51+
}
52+
53+
run()
54+

0 commit comments

Comments
 (0)