Skip to content

Commit 44fc97d

Browse files
committed
[NFC] Move the updateForUse API into PrunedliveRange
PrunedLiveness only knows about the live blocks and uses. The PrunedLiveRange subclass is now responsible for updating liveness based on both the defs and uses. This is in preparation for handling non-SSA liveness when uses occur before the first def.
1 parent 8a0d5ff commit 44fc97d

File tree

4 files changed

+111
-96
lines changed

4 files changed

+111
-96
lines changed

include/swift/SIL/PrunedLiveness.h

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,11 @@ class PrunedLiveBlocks {
225225
}
226226

227227
/// Update this liveness result for a single use.
228+
///
229+
/// Note: this never propagates liveness upward beyond def blocks.
230+
/// PrunedLiveRange knows about defs and whether it is possible for a
231+
/// block to contain a use before the first def. It must check that case
232+
/// separately and propagate liveness to predecessors.
228233
IsLive updateForUse(SILInstruction *user) {
229234
assert(isInitialized() && "at least one definition must be initialized");
230235

@@ -344,6 +349,7 @@ struct LiveRangeSummary {
344349
/// necessarily include liveness up to destroy_value or end_borrow
345350
/// instructions.
346351
class PrunedLiveness {
352+
protected:
347353
PrunedLiveBlocks liveBlocks;
348354

349355
// Map all "interesting" user instructions in this def's live range to a flag
@@ -386,27 +392,6 @@ class PrunedLiveness {
386392
liveBlocks.initializeDefBlock(defBB);
387393
}
388394

389-
/// For flexibility, \p lifetimeEnding is provided by the
390-
/// caller. PrunedLiveness makes no assumptions about the def-use
391-
/// relationships that generate liveness. For example, use->isLifetimeEnding()
392-
/// cannot distinguish the end of the borrow scope that defines this extended
393-
/// live range vs. a nested borrow scope within the extended live range.
394-
void updateForUse(SILInstruction *user, bool lifetimeEnding);
395-
396-
/// Updates the liveness for a whole borrow scope, beginning at \p op.
397-
/// Returns false if this cannot be done. This assumes that nested OSSA
398-
/// lifetimes are complete.
399-
InnerBorrowKind updateForBorrowingOperand(Operand *operand);
400-
401-
/// Update liveness for an interior pointer use. These are normally handled
402-
/// like an instantaneous use. But if \p operand "borrows" a value for the
403-
/// duration of a scoped address (store_borrow), then update liveness for the
404-
/// entire scope. This assumes that nested OSSA lifetimes are complete.
405-
AddressUseKind checkAndUpdateInteriorPointer(Operand *operand);
406-
407-
/// Update this liveness to extend across the given liveness.
408-
void extendAcrossLiveness(PrunedLiveness &otherLiveness);
409-
410395
PrunedLiveBlocks::IsLive getBlockLiveness(SILBasicBlock *bb) const {
411396
return liveBlocks.getBlockLiveness(bb);
412397
}
@@ -561,6 +546,27 @@ class PrunedLiveRange : public PrunedLiveness {
561546
SILValue value);
562547

563548
public:
549+
/// For flexibility, \p lifetimeEnding is provided by the
550+
/// caller. PrunedLiveness makes no assumptions about the def-use
551+
/// relationships that generate liveness. For example, use->isLifetimeEnding()
552+
/// cannot distinguish the end of the borrow scope that defines this extended
553+
/// live range vs. a nested borrow scope within the extended live range.
554+
void updateForUse(SILInstruction *user, bool lifetimeEnding);
555+
556+
/// Updates the liveness for a whole borrow scope, beginning at \p op.
557+
/// Returns false if this cannot be done. This assumes that nested OSSA
558+
/// lifetimes are complete.
559+
InnerBorrowKind updateForBorrowingOperand(Operand *operand);
560+
561+
/// Update liveness for an interior pointer use. These are normally handled
562+
/// like an instantaneous use. But if \p operand "borrows" a value for the
563+
/// duration of a scoped address (store_borrow), then update liveness for the
564+
/// entire scope. This assumes that nested OSSA lifetimes are complete.
565+
AddressUseKind checkAndUpdateInteriorPointer(Operand *operand);
566+
567+
/// Update this liveness to extend across the given liveness.
568+
void extendAcrossLiveness(PrunedLiveness &otherLiveness);
569+
564570
/// Update liveness for all direct uses of \p def. Transitively follows
565571
/// guaranteed forwards up to but not including guaranteed phis. If \p def is
566572
/// used by a guaranteed phi return InnerBorrowKind::Reborrowed.

include/swift/SIL/ScopedAddressUtils.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ struct ScopedAddressValue {
119119
///
120120
/// Valid for any type of liveness, SSA or MultiDef, that may be used by a
121121
/// scoped address.
122-
AddressUseKind updateTransitiveLiveness(PrunedLiveness &liveness) const;
122+
AddressUseKind updateTransitiveLiveness(SSAPrunedLiveness &liveness) const;
123123

124124
/// Create appropriate scope ending instruction at \p insertPt.
125125
void createScopeEnd(SILBasicBlock::iterator insertPt, SILLocation loc) const;

lib/SIL/Utils/PrunedLiveness.cpp

Lines changed: 81 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -51,79 +51,9 @@ void PrunedLiveBlocks::computeUseBlockLiveness(SILBasicBlock *userBB) {
5151
}
5252

5353
//===----------------------------------------------------------------------===//
54-
// MARK: PrunedLiveness
54+
// PrunedLiveBlocks and PrunedLiveness
5555
//===----------------------------------------------------------------------===//
5656

57-
void PrunedLiveness::updateForUse(SILInstruction *user, bool lifetimeEnding) {
58-
liveBlocks.updateForUse(user);
59-
60-
// Note that a user may use the current value from multiple operands. If any
61-
// of the uses are non-lifetime-ending, then we must consider the user
62-
// itself non-lifetime-ending; it cannot be a final destroy point because
63-
// the value of the non-lifetime-ending operand must be kept alive until the
64-
// end of the user. Consider a call that takes the same value using
65-
// different conventions:
66-
//
67-
// apply %f(%val, %val) : $(@guaranteed, @owned) -> ()
68-
//
69-
// This call is not considered the end of %val's lifetime. The @owned
70-
// argument must be copied.
71-
auto iterAndSuccess = users.insert({user, lifetimeEnding});
72-
if (!iterAndSuccess.second)
73-
iterAndSuccess.first->second &= lifetimeEnding;
74-
}
75-
76-
InnerBorrowKind PrunedLiveness::updateForBorrowingOperand(Operand *operand) {
77-
assert(operand->getOperandOwnership() == OperandOwnership::Borrow);
78-
79-
// A nested borrow scope is considered a use-point at each scope ending
80-
// instruction.
81-
//
82-
// Note: Ownership liveness should follow reborrows that are dominated by the
83-
// ownership definition.
84-
if (!BorrowingOperand(operand).visitScopeEndingUses([this](Operand *end) {
85-
if (end->getOperandOwnership() == OperandOwnership::Reborrow) {
86-
return false;
87-
}
88-
updateForUse(end->getUser(), /*lifetimeEnding*/ false);
89-
return true;
90-
})) {
91-
return InnerBorrowKind::Reborrowed;
92-
}
93-
return InnerBorrowKind::Contained;
94-
}
95-
96-
AddressUseKind PrunedLiveness::checkAndUpdateInteriorPointer(Operand *operand) {
97-
assert(operand->getOperandOwnership() == OperandOwnership::InteriorPointer);
98-
99-
if (auto scopedAddress = ScopedAddressValue::forUse(operand)) {
100-
scopedAddress.visitScopeEndingUses([this](Operand *end) {
101-
updateForUse(end->getUser(), /*lifetimeEnding*/ false);
102-
return true;
103-
});
104-
return AddressUseKind::NonEscaping;
105-
}
106-
// FIXME: findTransitiveUses should be a visitor so we're not recursively
107-
// allocating use vectors and potentially merging the use points.
108-
SmallVector<Operand *, 8> uses;
109-
auto useKind = InteriorPointerOperand(operand).findTransitiveUses(&uses);
110-
for (auto *use : uses) {
111-
updateForUse(use->getUser(), /*lifetimeEnding*/ false);
112-
}
113-
if (uses.empty()) {
114-
// Handle a dead address
115-
updateForUse(operand->getUser(), /*lifetimeEnding*/ false);
116-
}
117-
return useKind;
118-
}
119-
120-
void PrunedLiveness::extendAcrossLiveness(PrunedLiveness &otherLivesness) {
121-
// update this liveness for all the interesting users in otherLiveness.
122-
for (std::pair<SILInstruction *, bool> userAndEnd : otherLivesness.users) {
123-
updateForUse(userAndEnd.first, userAndEnd.second);
124-
}
125-
}
126-
12757
llvm::StringRef PrunedLiveBlocks::getStringRef(IsLive isLive) const {
12858
switch (isLive) {
12959
case Dead:
@@ -235,6 +165,85 @@ void PrunedLivenessBoundary::visitInsertionPoints(
235165
// PrunedLiveRange
236166
//===----------------------------------------------------------------------===//
237167

168+
template <typename LivenessWithDefs>
169+
void PrunedLiveRange<LivenessWithDefs>::updateForUse(SILInstruction *user,
170+
bool lifetimeEnding) {
171+
liveBlocks.updateForUse(user);
172+
173+
// Note that a user may use the current value from multiple operands. If any
174+
// of the uses are non-lifetime-ending, then we must consider the user
175+
// itself non-lifetime-ending; it cannot be a final destroy point because
176+
// the value of the non-lifetime-ending operand must be kept alive until the
177+
// end of the user. Consider a call that takes the same value using
178+
// different conventions:
179+
//
180+
// apply %f(%val, %val) : $(@guaranteed, @owned) -> ()
181+
//
182+
// This call is not considered the end of %val's lifetime. The @owned
183+
// argument must be copied.
184+
auto iterAndSuccess = users.insert({user, lifetimeEnding});
185+
if (!iterAndSuccess.second)
186+
iterAndSuccess.first->second &= lifetimeEnding;
187+
}
188+
189+
template <typename LivenessWithDefs>
190+
InnerBorrowKind
191+
PrunedLiveRange<LivenessWithDefs>::updateForBorrowingOperand(Operand *operand) {
192+
assert(operand->getOperandOwnership() == OperandOwnership::Borrow);
193+
194+
// A nested borrow scope is considered a use-point at each scope ending
195+
// instruction.
196+
//
197+
// Note: Ownership liveness should follow reborrows that are dominated by the
198+
// ownership definition.
199+
if (!BorrowingOperand(operand).visitScopeEndingUses([this](Operand *end) {
200+
if (end->getOperandOwnership() == OperandOwnership::Reborrow) {
201+
return false;
202+
}
203+
updateForUse(end->getUser(), /*lifetimeEnding*/ false);
204+
return true;
205+
})) {
206+
return InnerBorrowKind::Reborrowed;
207+
}
208+
return InnerBorrowKind::Contained;
209+
}
210+
211+
template <typename LivenessWithDefs>
212+
AddressUseKind PrunedLiveRange<LivenessWithDefs>::checkAndUpdateInteriorPointer(
213+
Operand *operand) {
214+
assert(operand->getOperandOwnership() == OperandOwnership::InteriorPointer);
215+
216+
if (auto scopedAddress = ScopedAddressValue::forUse(operand)) {
217+
scopedAddress.visitScopeEndingUses([this](Operand *end) {
218+
updateForUse(end->getUser(), /*lifetimeEnding*/ false);
219+
return true;
220+
});
221+
return AddressUseKind::NonEscaping;
222+
}
223+
// FIXME: findTransitiveUses should be a visitor so we're not recursively
224+
// allocating use vectors and potentially merging the use points.
225+
SmallVector<Operand *, 8> uses;
226+
auto useKind = InteriorPointerOperand(operand).findTransitiveUses(&uses);
227+
for (auto *use : uses) {
228+
updateForUse(use->getUser(), /*lifetimeEnding*/ false);
229+
}
230+
if (uses.empty()) {
231+
// Handle a dead address
232+
updateForUse(operand->getUser(), /*lifetimeEnding*/ false);
233+
}
234+
return useKind;
235+
}
236+
237+
template <typename LivenessWithDefs>
238+
void PrunedLiveRange<LivenessWithDefs>::extendAcrossLiveness(
239+
PrunedLiveness &otherLiveness) {
240+
// update this liveness for all the interesting users in otherLiveness.
241+
for (std::pair<SILInstruction *, bool> userAndEnd :
242+
otherLiveness.getAllUsers()) {
243+
updateForUse(userAndEnd.first, userAndEnd.second);
244+
}
245+
}
246+
238247
template <typename LivenessWithDefs>
239248
LiveRangeSummary PrunedLiveRange<LivenessWithDefs>::updateForDef(SILValue def) {
240249
ValueSet visited(def->getFunction());
@@ -606,7 +615,7 @@ LiveRangeSummary MultiDefPrunedLiveness::computeSimple() {
606615
// liveness, clients should check uses that are in PrunedLivenessBoundary.
607616
void DiagnosticPrunedLiveness::
608617
updateForUse(SILInstruction *user, bool lifetimeEnding) {
609-
PrunedLiveness::updateForUse(user, 0);
618+
SSAPrunedLiveness::updateForUse(user, 0);
610619

611620
auto useBlockLive = getBlockLiveness(user->getParent());
612621
// Record all uses of blocks on the liveness boundary. For blocks marked

lib/SIL/Utils/ScopedAddressUtils.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ AddressUseKind ScopedAddressValue::computeTransitiveLiveness(
105105
return updateTransitiveLiveness(liveness);
106106
}
107107

108-
AddressUseKind
109-
ScopedAddressValue::updateTransitiveLiveness(PrunedLiveness &liveness) const {
108+
AddressUseKind ScopedAddressValue::updateTransitiveLiveness(
109+
SSAPrunedLiveness &liveness) const {
110110
SmallVector<Operand *, 4> uses;
111111
// Collect all uses that need to be enclosed by the scope.
112112
auto addressKind = findTransitiveUsesForAddress(value, &uses);

0 commit comments

Comments
 (0)