Skip to content

Commit 4bc9671

Browse files
authored
Merge pull request #64926 from atrick/fix-liveblocks
MultiDefPrunedLiveness: add support for arbitrary uses/defs.
2 parents 967bd05 + c9eb1ae commit 4bc9671

File tree

6 files changed

+171
-120
lines changed

6 files changed

+171
-120
lines changed

include/swift/SIL/PrunedLiveness.h

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,6 @@ class PrunedLiveBlocks {
190190
/// Optional vector of live blocks for clients that deterministically iterate.
191191
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr;
192192

193-
/// Only a clean bitfield can be initialized.
194-
bool cleanFlag = true;
195-
196193
/// Once the first def has been initialized, uses can be added.
197194
bool initializedFlag = false;
198195

@@ -207,7 +204,6 @@ class PrunedLiveBlocks {
207204

208205
void invalidate() {
209206
initializedFlag = false;
210-
cleanFlag = false;
211207
}
212208

213209
void initializeDiscoveredBlocks(
@@ -229,17 +225,21 @@ class PrunedLiveBlocks {
229225
}
230226

231227
/// Update this liveness result for a single use.
232-
IsLive updateForUse(SILInstruction *user) {
228+
///
229+
/// \p isUseBeforeDef is true if \p user occures before the first def in this
230+
/// block. This indicates "liveness holes" inside the block, causing liveness
231+
/// to propagate to predecessors.
232+
IsLive updateForUse(SILInstruction *user, bool isUseBeforeDef) {
233233
assert(isInitialized() && "at least one definition must be initialized");
234234

235235
auto *block = user->getParent();
236-
auto liveness = getBlockLiveness(block);
237-
// If a block is already marked live, assume that liveness was propagated to
238-
// its predecessors. This assumes that uses will never be added above a def
239-
// in the same block.
240-
if (liveness != Dead)
241-
return liveness;
242-
236+
if (!isUseBeforeDef) {
237+
auto liveness = getBlockLiveness(block);
238+
// If a block is already marked live, it must either "kill" liveness, or
239+
// liveness was already propagated to its predecessors.
240+
if (liveness != Dead)
241+
return liveness;
242+
}
243243
computeUseBlockLiveness(block);
244244
return getBlockLiveness(block);
245245
}
@@ -348,6 +348,7 @@ struct LiveRangeSummary {
348348
/// necessarily include liveness up to destroy_value or end_borrow
349349
/// instructions.
350350
class PrunedLiveness {
351+
protected:
351352
PrunedLiveBlocks liveBlocks;
352353

353354
// Map all "interesting" user instructions in this def's live range to a flag
@@ -390,27 +391,6 @@ class PrunedLiveness {
390391
liveBlocks.initializeDefBlock(defBB);
391392
}
392393

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

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

664+
/// In SSA, uses never occur before the single def.
665+
bool isUserBeforeDef(SILInstruction *user) const { return false; }
666+
663667
/// SSA implementation of computeBoundary.
664668
void findBoundariesInBlock(SILBasicBlock *block, bool isLiveOut,
665669
PrunedLivenessBoundary &boundary) const;
@@ -711,7 +715,7 @@ class MultiDefPrunedLiveness : public PrunedLiveRange<MultiDefPrunedLiveness> {
711715
}
712716

713717
void initializeDef(SILInstruction *defInst) {
714-
initializeDefNode(defInst->asSILNode());
718+
initializeDefNode(cast<SILNode>(defInst));
715719
}
716720

717721
void initializeDef(SILArgument *defArg) { initializeDefNode(defArg); }
@@ -733,10 +737,25 @@ class MultiDefPrunedLiveness : public PrunedLiveRange<MultiDefPrunedLiveness> {
733737
return defs.contains(cast<SILNode>(inst));
734738
}
735739

740+
bool isDef(SILArgument *arg) const {
741+
return defs.contains(arg);
742+
}
743+
736744
bool isDefBlock(SILBasicBlock *block) const {
737745
return defBlocks.contains(block);
738746
}
739747

748+
/// Return true if \p user occurs before the first def in the same basic
749+
/// block. In classical liveness dataflow terms, gen/kill conditions over all
750+
/// users in 'bb' are:
751+
///
752+
/// Gen(bb) |= !isDefBlock(bb) || isUserBeforeDef(bb)
753+
/// Kill(bb) &= isDefBlock(bb) && !isUserBeforeDef(bb)
754+
///
755+
/// If 'bb' has no users, it is neither a Gen nor Kill. Otherwise, Gen and
756+
/// Kill are complements.
757+
bool isUserBeforeDef(SILInstruction *user) const;
758+
740759
/// Multi-Def implementation of computeBoundary.
741760
void findBoundariesInBlock(SILBasicBlock *block, bool isLiveOut,
742761
PrunedLivenessBoundary &boundary) const;

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/OSSALifetimeCompletion.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
///
1616
/// Interior liveness handles the following cases naturally:
1717
///
18-
/// When completing the lifetime if the initial value, %v1, transitively
19-
/// include all dominated reborrows. %phi1 in this example:
18+
/// When completing the lifetime of the initial value, %v1, transitively
19+
/// include all uses of dominated reborrows as, such as %phi1 in this example:
2020
///
2121
/// %v1 = ...
2222
/// cond_br bb1, bb2
@@ -31,8 +31,8 @@
3131
/// end_borrow %phi1
3232
/// %k1 = destroy_value %v1 // must be below end_borrow %phi1
3333
///
34-
/// When completing the lifetime for a (%phi2) transitively include all inner
35-
/// adjacent reborrows (%phi1):
34+
/// When completing the lifetime for a phi (%phi2) transitively include all
35+
/// uses of inner adjacent reborrows, such as %phi1 in this example:
3636
///
3737
/// bb1:
3838
/// %v1 = ...

0 commit comments

Comments
 (0)