Skip to content

Commit 5383df7

Browse files
committed
[NFC] LifetimeCompletion: Clarify modes.
Completion is done along a boundary, either the availability or the liveness boundary. Represent which with a type. Update docs and names.
1 parent fcd25ea commit 5383df7

File tree

5 files changed

+74
-29
lines changed

5 files changed

+74
-29
lines changed

include/swift/SIL/OSSALifetimeCompletion.h

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,22 +50,54 @@ class OSSALifetimeCompletion {
5050
OSSALifetimeCompletion(SILFunction *function, const DominanceInfo *domInfo)
5151
: domInfo(domInfo), completedValues(function) {}
5252

53+
// The kind of boundary at which to complete the lifetime.
54+
//
55+
// Liveness: "As early as possible." Consume the value after the last
56+
// non-consuming uses.
57+
// Availability: "As late as possible." Consume the value in the last blocks
58+
// beyond the non-consuming uses in which the value has been
59+
// consumed on no incoming paths.
60+
struct Boundary {
61+
enum Value : uint8_t {
62+
Liveness,
63+
Availability,
64+
};
65+
Value value;
66+
67+
Boundary(Value value) : value(value){};
68+
operator Value() const { return value; }
69+
70+
static std::optional<Boundary> getForcingLiveness(bool force) {
71+
if (!force)
72+
return {};
73+
return {Liveness};
74+
}
75+
76+
bool isLiveness() { return value == Liveness; }
77+
bool isAvailable() { return !isLiveness(); }
78+
};
79+
5380
/// Insert a lifetime-ending instruction on every path to complete the OSSA
54-
/// lifetime of \p value. Lifetime completion is only relevant for owned
81+
/// lifetime of \p value along \p boundary.
82+
///
83+
/// If \p boundary is not specified, the following boundary will be used:
84+
/// \p value is lexical -> Boundary::Availability
85+
/// \p value is non-lexical -> Boundary::Liveness
86+
///
87+
/// Lifetime completion is only relevant for owned
5588
/// values or borrow introducers.
56-
/// For lexical values lifetime is completed at unreachable instructions.
57-
/// For non-lexical values lifetime is completed at the lifetime boundary.
58-
/// When \p forceBoundaryCompletion is true, the client is able to guarantee
59-
/// that lifetime completion of lexical values at the lifetime boundary is
60-
/// sufficient.
61-
/// Currently \p forceBoundaryCompletion is used by mem2reg and temprvalueopt
62-
/// to complete lexical enum values on trivial paths.
89+
///
90+
/// Currently \p boundary == {Boundary::Availability} is used by Mem2Reg and
91+
/// TempRValueOpt and PredicatbleMemOpt to complete lexical enum values on
92+
/// trivial paths.
93+
///
6394
/// Returns true if any new instructions were created to complete the
6495
/// lifetime.
6596
///
6697
/// TODO: We also need to complete scoped addresses (e.g. store_borrow)!
6798
LifetimeCompletion
68-
completeOSSALifetime(SILValue value, bool forceBoundaryCompletion = false) {
99+
completeOSSALifetime(SILValue value,
100+
std::optional<Boundary> boundary = std::nullopt) {
69101
if (value->getOwnershipKind() == OwnershipKind::None)
70102
return LifetimeCompletion::NoLifetime;
71103

@@ -80,7 +112,7 @@ class OSSALifetimeCompletion {
80112
if (!completedValues.insert(value))
81113
return LifetimeCompletion::AlreadyComplete;
82114

83-
return analyzeAndUpdateLifetime(value, forceBoundaryCompletion)
115+
return analyzeAndUpdateLifetime(value, boundary)
84116
? LifetimeCompletion::WasCompleted
85117
: LifetimeCompletion::AlreadyComplete;
86118
}
@@ -90,7 +122,8 @@ class OSSALifetimeCompletion {
90122
llvm::function_ref<void(SILInstruction *)> visit);
91123

92124
protected:
93-
bool analyzeAndUpdateLifetime(SILValue value, bool forceBoundaryCompletion);
125+
bool analyzeAndUpdateLifetime(SILValue value,
126+
std::optional<Boundary> boundary);
94127
};
95128

96129
//===----------------------------------------------------------------------===//

lib/SIL/Utils/OSSALifetimeCompletion.cpp

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ static SILInstruction *endOSSALifetime(SILValue value, SILBuilder &builder) {
7070
return builder.createEndBorrow(loc, value);
7171
}
7272

73-
static bool endLifetimeAtBoundary(SILValue value,
74-
const SSAPrunedLiveness &liveness) {
73+
static bool endLifetimeAtLivenessBoundary(SILValue value,
74+
const SSAPrunedLiveness &liveness) {
7575
PrunedLivenessBoundary boundary;
7676
liveness.computeBoundary(boundary);
7777

@@ -322,8 +322,9 @@ void OSSALifetimeCompletion::visitUnreachableLifetimeEnds(
322322
visitor.visitAvailabilityBoundary(result, visit);
323323
}
324324

325-
static bool endLifetimeAtUnreachableBlocks(SILValue value,
326-
const SSAPrunedLiveness &liveness) {
325+
static bool
326+
endLifetimeAtAvailabilityBoundary(SILValue value,
327+
const SSAPrunedLiveness &liveness) {
327328
bool changed = false;
328329
OSSALifetimeCompletion::visitUnreachableLifetimeEnds(
329330
value, liveness, [&](auto *unreachable) {
@@ -337,12 +338,8 @@ static bool endLifetimeAtUnreachableBlocks(SILValue value,
337338
/// End the lifetime of \p value at unreachable instructions.
338339
///
339340
/// Returns true if any new instructions were created to complete the lifetime.
340-
///
341-
/// This is only meant to cleanup lifetimes that lead to dead-end blocks. After
342-
/// recursively completing all nested scopes, it then simply ends the lifetime
343-
/// at the Unreachable instruction.
344341
bool OSSALifetimeCompletion::analyzeAndUpdateLifetime(
345-
SILValue value, bool forceBoundaryCompletion) {
342+
SILValue value, std::optional<Boundary> maybeBoundary) {
346343
// Called for inner borrows, inner adjacent reborrows, inner reborrows, and
347344
// scoped addresses.
348345
auto handleInnerScope = [this](SILValue innerBorrowedValue) {
@@ -351,11 +348,17 @@ bool OSSALifetimeCompletion::analyzeAndUpdateLifetime(
351348
InteriorLiveness liveness(value);
352349
liveness.compute(domInfo, handleInnerScope);
353350

351+
Boundary boundary = maybeBoundary.value_or(
352+
value->isLexical() ? Boundary::Availability : Boundary::Liveness);
353+
354354
bool changed = false;
355-
if (value->isLexical() && !forceBoundaryCompletion) {
356-
changed |= endLifetimeAtUnreachableBlocks(value, liveness.getLiveness());
357-
} else {
358-
changed |= endLifetimeAtBoundary(value, liveness.getLiveness());
355+
switch (boundary) {
356+
case Boundary::Availability:
357+
changed |= endLifetimeAtAvailabilityBoundary(value, liveness.getLiveness());
358+
break;
359+
case Boundary::Liveness:
360+
changed |= endLifetimeAtLivenessBoundary(value, liveness.getLiveness());
361+
break;
359362
}
360363
// TODO: Rebuild outer adjacent phis on demand (SILGen does not currently
361364
// produce guaranteed phis). See FindEnclosingDefs &
@@ -374,9 +377,15 @@ static FunctionTest OSSALifetimeCompletionTest(
374377
"ossa-lifetime-completion",
375378
[](auto &function, auto &arguments, auto &test) {
376379
SILValue value = arguments.takeValue();
380+
std::optional<OSSALifetimeCompletion::Boundary> kind = std::nullopt;
381+
if (arguments.hasUntaken()) {
382+
kind = arguments.takeBool()
383+
? OSSALifetimeCompletion::Boundary::Liveness
384+
: OSSALifetimeCompletion::Boundary::Availability;
385+
}
377386
llvm::outs() << "OSSA lifetime completion: " << value;
378387
OSSALifetimeCompletion completion(&function, /*domInfo*/ nullptr);
379-
completion.completeOSSALifetime(value);
388+
completion.completeOSSALifetime(value, kind);
380389
function.print(llvm::outs());
381390
});
382391
} // end namespace swift::test

lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2673,10 +2673,11 @@ bool AllocOptimize::tryToRemoveDeadAllocation() {
26732673
// Lexical enums can have incomplete lifetimes in non payload paths that
26742674
// don't end in unreachable. Force their lifetime to end immediately after
26752675
// the last use instead.
2676-
bool forceBoundaryCompletion = v->getType().isOrHasEnum();
2676+
auto boundary = OSSALifetimeCompletion::Boundary::getForcingLiveness(
2677+
v->getType().isOrHasEnum());
26772678
LLVM_DEBUG(llvm::dbgs() << "Completing lifetime of: ");
26782679
LLVM_DEBUG(v->dump());
2679-
completion.completeOSSALifetime(v, forceBoundaryCompletion);
2680+
completion.completeOSSALifetime(v, boundary);
26802681
}
26812682

26822683
return true;

lib/SILOptimizer/Transforms/SILMem2Reg.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1754,7 +1754,8 @@ void StackAllocationPromoter::run(BasicBlockSetVector &livePhiBlocks) {
17541754
for (auto it : valuesToComplete) {
17551755
// Set forceBoundaryCompletion as true so that we complete at boundary for
17561756
// lexical values as well.
1757-
completion.completeOSSALifetime(it, /* forceBoundaryCompletion */ true);
1757+
completion.completeOSSALifetime(it,
1758+
OSSALifetimeCompletion::Boundary::Liveness);
17581759
}
17591760
}
17601761

lib/SILOptimizer/Transforms/TempRValueElimination.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -964,7 +964,8 @@ void TempRValueOptPass::run() {
964964
// Call the utlity to complete ossa lifetime.
965965
OSSALifetimeCompletion completion(function, da->get(function));
966966
for (auto it : valuesToComplete) {
967-
completion.completeOSSALifetime(it, /* forceBoundaryCompletion */ true);
967+
completion.completeOSSALifetime(it,
968+
OSSALifetimeCompletion::Boundary::Liveness);
968969
}
969970
}
970971

0 commit comments

Comments
 (0)