Skip to content

Commit 3bb1766

Browse files
committed
[SILOptimizer] Keep lexical lifetime markers.
Previously, TempRValueElimination would peephole simple alloc_stacks, even when they were lexical; here, they are left for Mem2Reg to properly handle. Previously, SemanticARCOpts would eliminate lexical begin_borrows, incorrectly allowing the lifetime of the value borrowed by them to be observably shortened. Here, those borrow scopes are not eliminated if they are lexical. Added an executable test that verifies that a local variable strongly referencing a delegate object keeps that delegate alive through the call to an object that weakly references the delegate and calls out to it.
1 parent 960cbcb commit 3bb1766

File tree

3 files changed

+72
-0
lines changed

3 files changed

+72
-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.
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)