Skip to content

Commit 6324067

Browse files
committed
[LifetimeCompletion] Add availability_with_leaks.
The new boundary allows for invalid OSSA where values are not consumed on paths leading to blocks that exit the function normally. In such cases, the value is allowed to continue leaking as before.
1 parent 518de7c commit 6324067

File tree

3 files changed

+62
-15
lines changed

3 files changed

+62
-15
lines changed

include/swift/SIL/OSSALifetimeCompletion.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,19 @@ class OSSALifetimeCompletion {
5757
// Availability: "As late as possible." Consume the value in the last blocks
5858
// beyond the non-consuming uses in which the value has been
5959
// consumed on no incoming paths.
60+
// AvailabilityWithLeaks: "As late as possible or later." Consume the value
61+
// in the last blocks beyond the non-consuming uses in
62+
// which the value has been consumed on no incoming
63+
// paths, unless that block's terminator isn't an
64+
// unreachable, in which case, don't consume it there.
65+
//
66+
// This boundary works around bugs where SILGen emits
67+
// illegal OSSA lifetimes.
6068
struct Boundary {
6169
enum Value : uint8_t {
6270
Liveness,
6371
Availability,
72+
AvailabilityWithLeaks,
6473
};
6574
Value value;
6675

@@ -106,8 +115,14 @@ class OSSALifetimeCompletion {
106115
: LifetimeCompletion::AlreadyComplete;
107116
}
108117

118+
enum AllowLeaks_t : bool {
119+
AllowLeaks = true,
120+
DoNotAllowLeaks = false,
121+
};
122+
109123
static void visitUnreachableLifetimeEnds(
110-
SILValue value, const SSAPrunedLiveness &liveness,
124+
SILValue value, AllowLeaks_t allowLeaks,
125+
const SSAPrunedLiveness &liveness,
111126
llvm::function_ref<void(SILInstruction *)> visit);
112127

113128
protected:
@@ -165,6 +180,9 @@ operator<<(llvm::raw_ostream &OS, OSSALifetimeCompletion::Boundary boundary) {
165180
case OSSALifetimeCompletion::Boundary::Availability:
166181
OS << "availability";
167182
break;
183+
case OSSALifetimeCompletion::Boundary::AvailabilityWithLeaks:
184+
OS << "availability_with_leaks";
185+
break;
168186
}
169187
return OS;
170188
}

lib/SIL/Utils/OSSALifetimeCompletion.cpp

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,21 @@ class VisitUnreachableLifetimeEnds {
122122
/// The value whose dead-end block lifetime ends are to be visited.
123123
SILValue value;
124124

125+
/// Whether to allow leaks.
126+
///
127+
/// Here, that entails allowing walks to reach non-unreachable terminators and
128+
/// not creating lifetime ends before them.
129+
OSSALifetimeCompletion::AllowLeaks_t allowLeaks;
130+
125131
/// The non-lifetime-ending boundary of `value`.
126132
BasicBlockSet starts;
127133
/// The region between (inclusive) the `starts` and the unreachable blocks.
128134
BasicBlockSetVector region;
129135

130136
public:
131-
VisitUnreachableLifetimeEnds(SILValue value)
132-
: value(value), starts(value->getFunction()),
137+
VisitUnreachableLifetimeEnds(SILValue value,
138+
OSSALifetimeCompletion::AllowLeaks_t allowLeaks)
139+
: value(value), allowLeaks(allowLeaks), starts(value->getFunction()),
133140
region(value->getFunction()) {}
134141

135142
/// Region discovery.
@@ -232,9 +239,17 @@ void VisitUnreachableLifetimeEnds::computeRegion(
232239
// (3) Forward walk to find the region in which `value` might be available.
233240
while (auto *block = regionWorklist.pop()) {
234241
if (block->succ_empty()) {
235-
// This assert will fail unless there is already a lifetime-ending
236-
// instruction on each path to normal function exits.
237-
assert(isa<UnreachableInst>(block->getTerminator()));
242+
// This is a function-exiting block.
243+
//
244+
// In valid-but-lifetime-incomplete OSSA there must be a lifetime-ending
245+
// instruction on each path from the def that exits the function normally.
246+
// Thus finding a value available at the end of such a block means that
247+
// the block does _not_ must not exits the function normally; in other
248+
// words its terminator must be an UnreachableInst.
249+
//
250+
// In invalid OSSA, indicated by the `allowLeaks` flag, no such guarantee
251+
// exists.
252+
assert(isa<UnreachableInst>(block->getTerminator()) || allowLeaks);
238253
}
239254
for (auto *successor : block->getSuccessorBlocks()) {
240255
regionWorklist.pushIfNotVisited(successor);
@@ -300,6 +315,12 @@ void VisitUnreachableLifetimeEnds::visitAvailabilityBoundary(
300315
if (!block->succ_empty() && !hasUnavailableSuccessor()) {
301316
continue;
302317
}
318+
if (allowLeaks && block->succ_empty() &&
319+
!isa<UnreachableInst>(block->getTerminator())) {
320+
// Availability extends to the end of a function-exiting-normally block.
321+
// If leaks are allowed, don't visit.
322+
continue;
323+
}
303324
assert(hasUnavailableSuccessor() ||
304325
isa<UnreachableInst>(block->getTerminator()));
305326
visit(block->getTerminator());
@@ -308,10 +329,10 @@ void VisitUnreachableLifetimeEnds::visitAvailabilityBoundary(
308329
} // end anonymous namespace
309330

310331
void OSSALifetimeCompletion::visitUnreachableLifetimeEnds(
311-
SILValue value, const SSAPrunedLiveness &liveness,
332+
SILValue value, AllowLeaks_t allowLeaks, const SSAPrunedLiveness &liveness,
312333
llvm::function_ref<void(SILInstruction *)> visit) {
313334

314-
VisitUnreachableLifetimeEnds visitor(value);
335+
VisitUnreachableLifetimeEnds visitor(value, allowLeaks);
315336

316337
visitor.computeRegion(liveness);
317338

@@ -322,12 +343,12 @@ void OSSALifetimeCompletion::visitUnreachableLifetimeEnds(
322343
visitor.visitAvailabilityBoundary(result, visit);
323344
}
324345

325-
static bool
326-
endLifetimeAtAvailabilityBoundary(SILValue value,
327-
const SSAPrunedLiveness &liveness) {
346+
static bool endLifetimeAtAvailabilityBoundary(
347+
SILValue value, OSSALifetimeCompletion::AllowLeaks_t allowLeaks,
348+
const SSAPrunedLiveness &liveness) {
328349
bool changed = false;
329350
OSSALifetimeCompletion::visitUnreachableLifetimeEnds(
330-
value, liveness, [&](auto *unreachable) {
351+
value, allowLeaks, liveness, [&](auto *unreachable) {
331352
SILBuilderWithScope builder(unreachable);
332353
endOSSALifetime(value, builder);
333354
changed = true;
@@ -354,7 +375,12 @@ bool OSSALifetimeCompletion::analyzeAndUpdateLifetime(SILValue value,
354375
changed |= endLifetimeAtLivenessBoundary(value, liveness.getLiveness());
355376
break;
356377
case Boundary::Availability:
357-
changed |= endLifetimeAtAvailabilityBoundary(value, liveness.getLiveness());
378+
changed |= endLifetimeAtAvailabilityBoundary(value, DoNotAllowLeaks,
379+
liveness.getLiveness());
380+
break;
381+
case Boundary::AvailabilityWithLeaks:
382+
changed |= endLifetimeAtAvailabilityBoundary(value, AllowLeaks,
383+
liveness.getLiveness());
358384
break;
359385
}
360386
// TODO: Rebuild outer adjacent phis on demand (SILGen does not currently
@@ -379,7 +405,9 @@ static FunctionTest OSSALifetimeCompletionTest(
379405
arguments.takeString())
380406
.Case("liveness", OSSALifetimeCompletion::Boundary::Liveness)
381407
.Case("availability",
382-
OSSALifetimeCompletion::Boundary::Availability);
408+
OSSALifetimeCompletion::Boundary::Availability)
409+
.Case("availability_with_leaks",
410+
OSSALifetimeCompletion::Boundary::AvailabilityWithLeaks);
383411
llvm::outs() << "OSSA lifetime completion on " << kind
384412
<< " boundary: " << value;
385413
OSSALifetimeCompletion completion(&function, /*domInfo*/ nullptr);

lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,8 @@ void CanonicalizeOSSALifetime::extendLivenessToDeinitBarriers() {
274274
}
275275

276276
OSSALifetimeCompletion::visitUnreachableLifetimeEnds(
277-
getCurrentDef(), completeLiveness, [&](auto *unreachable) {
277+
getCurrentDef(), OSSALifetimeCompletion::DoNotAllowLeaks,
278+
completeLiveness, [&](auto *unreachable) {
278279
recordUnreachableLifetimeEnd(unreachable);
279280
unreachable->visitPriorInstructions([&](auto *inst) {
280281
liveness->extendToNonUse(inst);

0 commit comments

Comments
 (0)