Skip to content

Commit 7713eef

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 4feae67 commit 7713eef

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-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<MarkUnresolvedNonCopyableValueInst *>);
8991
void checkAddresses();
9092
};
9193

@@ -110,10 +112,75 @@ void MoveOnlyChecker::checkObjects() {
110112
return;
111113
}
112114

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

121+
void MoveOnlyChecker::completeObjectLifetimes(
122+
ArrayRef<MarkUnresolvedNonCopyableValueInst *> insts) {
123+
// TODO: Delete once OSSALifetimeCompletion is run as part of SILGenCleanup.
124+
OSSALifetimeCompletion completion(fn, domTree);
125+
126+
// Collect all values derived from each mark_unresolved_non_copyable_value
127+
// instruction via ownership instructions and phis.
128+
ValueWorklist transitiveValues(fn);
129+
for (auto *inst : insts) {
130+
transitiveValues.push(inst);
131+
}
132+
while (auto value = transitiveValues.pop()) {
133+
for (auto *use : value->getUses()) {
134+
auto *user = use->getUser();
135+
switch (user->getKind()) {
136+
case SILInstructionKind::BeginBorrowInst:
137+
case SILInstructionKind::CopyValueInst:
138+
case SILInstructionKind::MoveValueInst:
139+
transitiveValues.pushIfNotVisited(cast<SingleValueInstruction>(user));
140+
break;
141+
case SILInstructionKind::BranchInst: {
142+
PhiOperand po(use);
143+
transitiveValues.pushIfNotVisited(po.getValue());
144+
break;
145+
}
146+
default: {
147+
auto forward = ForwardingOperation(user);
148+
if (!forward)
149+
continue;
150+
forward.visitForwardedValues([&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+
assert(!arg->isReborrow() && "reborrows not legal at this SIL stage");
174+
if (!transitiveValues.isVisited(arg))
175+
continue;
176+
if (completion.completeOSSALifetime(arg) ==
177+
LifetimeCompletion::WasCompleted) {
178+
madeChange = true;
179+
}
180+
}
181+
}
182+
}
183+
117184
void MoveOnlyChecker::checkAddresses() {
118185
unsigned diagCount = diagnosticEmitter.getDiagnosticCount();
119186
SmallSetVector<MarkUnresolvedNonCopyableValueInst *, 32>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %s -o %t/bin
3+
// RUN: %target-run %t/bin 2> %t/out.txt || true
4+
// RUN: %FileCheck %s < %t/out.txt
5+
6+
struct Example: ~Copyable {
7+
private var failureString: String { "Goodbye." }
8+
deinit { fatalError("FATAL ERROR: \(failureString)") }
9+
}
10+
11+
func doit() {
12+
let e = Example()
13+
// CHECK: FATAL ERROR: Goodbye.
14+
}
15+
16+
doit()

0 commit comments

Comments
 (0)