Skip to content

Commit 6b77b79

Browse files
committed
MultiDefPrunedLiveness: add support for arbitrary uses/defs.
This now supports liveness holes within blocks where previously we expected a phi def. PrunedLiveness is streamlined for SSA liveness where we expect the defs and uses to be related in the SSA def-use graph. MultiDefPrunedLiveness was added on top of SSAPrunedLivenes to handle extended SSA liveness that occurs with phis, but are still connected in the def-use graph. Recently MultiDefPrunedLiveness was repurposed for liveness of addressible storage. This means that we can now have uses before defs in the same block without a corresponding phi. Fortunately, handling this case is a straightforward extension of MultiDefPrunedLiveness that does not complicate or penalize the streamlined SSA case.
1 parent 44fc97d commit 6b77b79

File tree

3 files changed

+59
-21
lines changed

3 files changed

+59
-21
lines changed

include/swift/SIL/PrunedLiveness.h

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -226,21 +226,20 @@ class PrunedLiveBlocks {
226226

227227
/// Update this liveness result for a single use.
228228
///
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.
233-
IsLive updateForUse(SILInstruction *user) {
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) {
234233
assert(isInitialized() && "at least one definition must be initialized");
235234

236235
auto *block = user->getParent();
237-
auto liveness = getBlockLiveness(block);
238-
// If a block is already marked live, assume that liveness was propagated to
239-
// its predecessors. This assumes that uses will never be added above a def
240-
// in the same block.
241-
if (liveness != Dead)
242-
return liveness;
243-
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+
}
244243
computeUseBlockLiveness(block);
245244
return getBlockLiveness(block);
246245
}
@@ -662,6 +661,9 @@ class SSAPrunedLiveness : public PrunedLiveRange<SSAPrunedLiveness> {
662661
return def->getParentBlock() == block;
663662
}
664663

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

715717
void initializeDef(SILInstruction *defInst) {
716-
initializeDefNode(defInst->asSILNode());
718+
initializeDefNode(cast<SILNode>(defInst));
717719
}
718720

719721
void initializeDef(SILArgument *defArg) { initializeDefNode(defArg); }
@@ -735,10 +737,25 @@ class MultiDefPrunedLiveness : public PrunedLiveRange<MultiDefPrunedLiveness> {
735737
return defs.contains(cast<SILNode>(inst));
736738
}
737739

740+
bool isDef(SILArgument *arg) const {
741+
return defs.contains(arg);
742+
}
743+
738744
bool isDefBlock(SILBasicBlock *block) const {
739745
return defBlocks.contains(block);
740746
}
741747

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+
742759
/// Multi-Def implementation of computeBoundary.
743760
void findBoundariesInBlock(SILBasicBlock *block, bool isLiveOut,
744761
PrunedLivenessBoundary &boundary) const;

lib/SIL/Utils/PrunedLiveness.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ void PrunedLivenessBoundary::visitInsertionPoints(
168168
template <typename LivenessWithDefs>
169169
void PrunedLiveRange<LivenessWithDefs>::updateForUse(SILInstruction *user,
170170
bool lifetimeEnding) {
171-
liveBlocks.updateForUse(user);
171+
liveBlocks.updateForUse(user, asImpl().isUserBeforeDef(user));
172172

173173
// Note that a user may use the current value from multiple operands. If any
174174
// of the uses are non-lifetime-ending, then we must consider the user
@@ -527,6 +527,29 @@ void SSAPrunedLiveness::findBoundariesInBlock(
527527
// MultiDefPrunedLiveness
528528
//===----------------------------------------------------------------------===//
529529

530+
bool MultiDefPrunedLiveness::isUserBeforeDef(SILInstruction *user) const {
531+
auto *block = user->getParent();
532+
if (!isDefBlock(block))
533+
return false;
534+
535+
if (llvm::any_of(block->getArguments(), [this](SILArgument *arg) {
536+
return isDef(arg);
537+
})) {
538+
return false;
539+
}
540+
541+
auto *current = user;
542+
while (true) {
543+
// If user is also a def, then the use is considered before the def.
544+
current = current->getPreviousInstruction();
545+
if (!current)
546+
return true;
547+
548+
if (isDef(current))
549+
return false;
550+
}
551+
}
552+
530553
void MultiDefPrunedLiveness::findBoundariesInBlock(
531554
SILBasicBlock *block, bool isLiveOut,
532555
PrunedLivenessBoundary &boundary) const {

test/SILOptimizer/liveness_unit.sil

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -507,22 +507,20 @@ bb0(%0 : @guaranteed $C):
507507
// CHECK-LABEL: begin running test 1 of 1 on testMultiDefUseAddressReinit
508508
// CHECK: MultiDef lifetime analysis:
509509
// CHECK: def instruction: store %{{.*}} to [init] [[ADR:%.*]] : $*C
510-
// CHECK: def instruction: destroy_addr [[ADR]] : $*C
511-
// CHECK: bb0: LiveWithin
510+
// CHECK: def instruction: store %0 to [init] [[ADR]] : $*C
511+
// CHECK: bb0: LiveOut
512512
// CHECK: bb1: LiveWithin
513513
// CHECK: regular user: %{{.*}} = load [copy] [[ADR]] : $*C
514514
// CHECK: last user: %{{.*}} = load [copy] [[ADR]] : $*C
515-
//
516-
// FIXME: This store is not really a dead def!
517-
// CHECK: dead def: store %2 to [init] %1 : $*C
518-
// CHECK: dead def: destroy_addr %1 : $*C
515+
// CHECK: boundary edge: bb2
516+
// CHECK: dead def: store %0 to [init] %1 : $*C
519517
// CHECK-LABEL: end running test 1 of 1 on testMultiDefUseAddressReinit
520518
sil [ossa] @testMultiDefUseAddressReinit : $@convention(thin) (@owned C) -> () {
521519
bb0(%0: @owned $C):
522520
%1 = alloc_stack $C
523521
%2 = copy_value %0 : $C
524522
test_specification """
525-
multidefuse-liveness defs: @instruction @block[1].instruction[1]
523+
multidefuse-liveness defs: @instruction @block[1].instruction[2]
526524
uses: @block[1].instruction[0]
527525
"""
528526
store %2 to [init] %1 : $*C

0 commit comments

Comments
 (0)