Skip to content

Commit 2bad5c7

Browse files
committed
[MoveValueChecker] Complete lifetimes first.
Before move-checking values, complete the lifetimes of all the values derived from them via copy, borrow, and move. Collect all such values and their derived transitive values and then complete the lifetimes of each, visiting the instructions which produce them in post-order. Once OSSALifetimeCommpletion runs as part of SILGenCleanup, this code can be deleted.
1 parent 2c8373c commit 2bad5c7

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

lib/SILOptimizer/Mandatory/MoveOnlyChecker.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "swift/SIL/FieldSensitivePrunedLiveness.h"
3131
#include "swift/SIL/InstructionUtils.h"
3232
#include "swift/SIL/MemAccessUtils.h"
33+
#include "swift/SIL/OSSALifetimeCompletion.h"
3334
#include "swift/SIL/OwnershipUtils.h"
3435
#include "swift/SIL/PrunedLiveness.h"
3536
#include "swift/SIL/SILArgument.h"
@@ -86,6 +87,7 @@ struct MoveOnlyChecker {
8687
}
8788

8889
void checkObjects();
90+
void completeObjectLifetimes(ArrayRef<MarkMustCheckInst *>);
8991
void checkAddresses();
9092
};
9193

@@ -109,10 +111,75 @@ void MoveOnlyChecker::checkObjects() {
109111
return;
110112
}
111113

114+
completeObjectLifetimes(moveIntroducersToProcess.getArrayRef());
115+
112116
MoveOnlyObjectChecker checker{diagnosticEmitter, domTree, poa, allocator};
113117
madeChange |= checker.check(moveIntroducersToProcess);
114118
}
115119

120+
void MoveOnlyChecker::completeObjectLifetimes(
121+
ArrayRef<MarkMustCheckInst *> insts) {
122+
// TODO: Delete once OSSALifetimeCompletion is run as part of SILGenCleanup.
123+
OSSALifetimeCompletion completion(fn, domTree);
124+
125+
// Collect all values derived from each mark_unresolved_non_copyable_value
126+
// instruction via ownership instructions and phis.
127+
ValueWorklist transitiveValues(fn);
128+
for (auto *inst : insts) {
129+
transitiveValues.push(inst);
130+
}
131+
while (auto value = transitiveValues.pop()) {
132+
for (auto *use : value->getUses()) {
133+
auto *user = use->getUser();
134+
switch (user->getKind()) {
135+
case SILInstructionKind::BeginBorrowInst:
136+
case SILInstructionKind::CopyValueInst:
137+
case SILInstructionKind::MoveValueInst:
138+
transitiveValues.pushIfNotVisited(cast<SingleValueInstruction>(user));
139+
break;
140+
case SILInstructionKind::BranchInst: {
141+
PhiOperand po(use);
142+
transitiveValues.pushIfNotVisited(po.getValue());
143+
break;
144+
}
145+
default: {
146+
auto *forward = OwnershipForwardingMixin::get(user);
147+
if (!forward)
148+
continue;
149+
OwnershipForwardingMixin::visitForwardedValues(
150+
user, [&transitiveValues](auto forwarded) {
151+
transitiveValues.pushIfNotVisited(forwarded);
152+
return true;
153+
});
154+
break;
155+
}
156+
}
157+
}
158+
}
159+
// Complete the lifetime of each collected value. This is a subset of the
160+
// work that SILGenCleanup will do.
161+
for (auto *block : poa->get(fn)->getPostOrder()) {
162+
for (SILInstruction &inst : reverse(*block)) {
163+
for (auto result : inst.getResults()) {
164+
if (!transitiveValues.isVisited(result))
165+
continue;
166+
if (completion.completeOSSALifetime(result) ==
167+
LifetimeCompletion::WasCompleted) {
168+
madeChange = true;
169+
}
170+
}
171+
}
172+
for (SILArgument *arg : block->getArguments()) {
173+
if (!transitiveValues.isVisited(arg))
174+
continue;
175+
if (completion.completeOSSALifetime(arg) ==
176+
LifetimeCompletion::WasCompleted) {
177+
madeChange = true;
178+
}
179+
}
180+
}
181+
}
182+
116183
void MoveOnlyChecker::checkAddresses() {
117184
unsigned diagCount = diagnosticEmitter.getDiagnosticCount();
118185
SmallSetVector<MarkMustCheckInst *, 32> moveIntroducersToProcess;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %s -o %t/bin
3+
// RUN: %target-codesign %t/bin
4+
// RUN: %target-run %t/bin 2> %t/out.txt || true
5+
// RUN: %FileCheck %s < %t/out.txt
6+
7+
// REQUIRES: executable_test
8+
9+
struct Example: ~Copyable {
10+
private var failureString: String { "Goodbye." }
11+
deinit { fatalError("FATAL ERROR: \(failureString)") }
12+
}
13+
14+
func doit() {
15+
let e = Example()
16+
// CHECK: FATAL ERROR: Goodbye.
17+
}
18+
19+
doit()

0 commit comments

Comments
 (0)