Skip to content

Commit 92fd8da

Browse files
committed
[FieldSensitivePL] Fixed use-before-def handling.
rdar://111118843
1 parent 7322ccb commit 92fd8da

File tree

5 files changed

+133
-102
lines changed

5 files changed

+133
-102
lines changed

include/swift/SIL/FieldSensitivePrunedLiveness.h

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -571,19 +571,22 @@ class FieldSensitivePrunedLiveBlocks {
571571
}
572572

573573
/// Update this liveness result for a single use.
574-
IsLive updateForUse(SILInstruction *user, unsigned bitNo) {
574+
IsLive updateForUse(SILInstruction *user, unsigned bitNo,
575+
bool isUserBeforeDef) {
575576
assert(isInitialized());
576577
auto *block = user->getParent();
577-
auto liveness = getBlockLiveness(block, bitNo);
578-
if (liveness != Dead)
579-
return liveness;
578+
if (!isUserBeforeDef) {
579+
auto liveness = getBlockLiveness(block, bitNo);
580+
if (liveness != Dead)
581+
return liveness;
582+
}
580583
computeScalarUseBlockLiveness(block, bitNo);
581584
return getBlockLiveness(block, bitNo);
582585
}
583586

584587
/// Update this range of liveness results for a single use.
585588
void updateForUse(SILInstruction *user, unsigned startBitNo,
586-
unsigned endBitNo,
589+
unsigned endBitNo, SmallBitVector const &useBeforeDefBits,
587590
SmallVectorImpl<IsLive> &resultingLiveness);
588591

589592
IsLive getBlockLiveness(SILBasicBlock *bb, unsigned bitNo) const {
@@ -854,10 +857,12 @@ class FieldSensitivePrunedLiveness {
854857
/// Also for flexibility, \p affectedAddress must be a derived projection from
855858
/// the base that \p user is affecting.
856859
void updateForUse(SILInstruction *user, TypeTreeLeafTypeRange span,
857-
bool lifetimeEnding);
860+
bool lifetimeEnding,
861+
SmallBitVector const &useBeforeDefBits);
858862

859863
void updateForUse(SILInstruction *user, SmallBitVector const &bits,
860-
bool lifetimeEnding);
864+
bool lifetimeEnding,
865+
SmallBitVector const &useBeforeDefBits);
861866

862867
void getBlockLiveness(SILBasicBlock *bb, TypeTreeLeafTypeRange span,
863868
SmallVectorImpl<FieldSensitivePrunedLiveBlocks::IsLive>
@@ -1160,6 +1165,12 @@ class FieldSensitiveSSAPrunedLiveRange
11601165
return def.first->getParentBlock() == block && def.second->contains(bit);
11611166
}
11621167

1168+
template <typename Iterable>
1169+
void isUserBeforeDef(SILInstruction *user, Iterable const &iterable,
1170+
SmallBitVector &useBeforeDefBits) const {
1171+
assert(useBeforeDefBits.none());
1172+
}
1173+
11631174
void
11641175
findBoundariesInBlock(SILBasicBlock *block, unsigned bitNo, bool isLiveOut,
11651176
FieldSensitivePrunedLivenessBoundary &boundary) const;
@@ -1243,6 +1254,36 @@ class FieldSensitiveMultiDefPrunedLiveRange
12431254
});
12441255
}
12451256

1257+
bool isDefBlock(SILBasicBlock *block, SmallBitVector const &bits) const {
1258+
assert(isInitialized());
1259+
auto iter = defBlocks.find(block);
1260+
if (!iter)
1261+
return false;
1262+
return llvm::any_of(*iter, [&](TypeTreeLeafTypeRange storedSpan) {
1263+
return storedSpan.intersects(bits);
1264+
});
1265+
}
1266+
1267+
/// Return true if \p user occurs before the first def in the same basic
1268+
/// block. In classical liveness dataflow terms, gen/kill conditions over all
1269+
/// users in 'bb' are:
1270+
///
1271+
/// Gen(bb) |= !isDefBlock(bb) || isUserBeforeDef(bb)
1272+
/// Kill(bb) &= isDefBlock(bb) && !isUserBeforeDef(bb)
1273+
///
1274+
/// If 'bb' has no users, it is neither a Gen nor Kill. Otherwise, Gen and
1275+
/// Kill are complements.
1276+
bool isUserBeforeDef(SILInstruction *user, unsigned element) const;
1277+
template <typename Iterable>
1278+
void isUserBeforeDef(SILInstruction *user, Iterable const &iterable,
1279+
SmallBitVector &useBeforeDefBits) const {
1280+
for (auto bit : iterable) {
1281+
if (isUserBeforeDef(user, bit)) {
1282+
useBeforeDefBits.set(bit);
1283+
}
1284+
}
1285+
}
1286+
12461287
bool isDef(SILInstruction *inst, unsigned bit) const {
12471288
assert(isInitialized());
12481289
auto iter = defs.find(cast<SILNode>(inst));

lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp

Lines changed: 47 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,7 @@ void FieldSensitivePrunedLiveBlocks::computeScalarUseBlockLiveness(
505505
/// Terminators are not live out of the block.
506506
void FieldSensitivePrunedLiveBlocks::updateForUse(
507507
SILInstruction *user, unsigned startBitNo, unsigned endBitNo,
508+
SmallBitVector const &useBeforeDefBits,
508509
SmallVectorImpl<IsLive> &resultingLivenessInfo) {
509510
assert(isInitialized());
510511
resultingLivenessInfo.clear();
@@ -517,10 +518,15 @@ void FieldSensitivePrunedLiveBlocks::updateForUse(
517518

518519
for (unsigned index : indices(resultingLivenessInfo)) {
519520
unsigned specificBitNo = startBitNo + index;
521+
auto isUseBeforeDef = useBeforeDefBits.test(specificBitNo);
520522
switch (resultingLivenessInfo[index]) {
521523
case LiveOut:
522524
case LiveWithin:
523-
continue;
525+
if (!isUseBeforeDef) {
526+
continue;
527+
} else {
528+
LLVM_FALLTHROUGH;
529+
}
524530
case Dead: {
525531
// This use block has not yet been marked live. Mark it and its
526532
// predecessor blocks live.
@@ -599,21 +605,21 @@ void FieldSensitivePrunedLivenessBoundary::dump() const {
599605
// MARK: FieldSensitiveLiveness
600606
//===----------------------------------------------------------------------===//
601607

602-
void FieldSensitivePrunedLiveness::updateForUse(SILInstruction *user,
603-
TypeTreeLeafTypeRange range,
604-
bool lifetimeEnding) {
608+
void FieldSensitivePrunedLiveness::updateForUse(
609+
SILInstruction *user, TypeTreeLeafTypeRange range, bool lifetimeEnding,
610+
SmallBitVector const &useBeforeDefBits) {
605611
SmallVector<FieldSensitivePrunedLiveBlocks::IsLive, 8> resultingLiveness;
606612
liveBlocks.updateForUse(user, range.startEltOffset, range.endEltOffset,
607-
resultingLiveness);
613+
useBeforeDefBits, resultingLiveness);
608614

609615
addInterestingUser(user, range, lifetimeEnding);
610616
}
611617

612-
void FieldSensitivePrunedLiveness::updateForUse(SILInstruction *user,
613-
SmallBitVector const &bits,
614-
bool lifetimeEnding) {
618+
void FieldSensitivePrunedLiveness::updateForUse(
619+
SILInstruction *user, SmallBitVector const &bits, bool lifetimeEnding,
620+
SmallBitVector const &useBeforeDefBits) {
615621
for (auto bit : bits.set_bits()) {
616-
liveBlocks.updateForUse(user, bit);
622+
liveBlocks.updateForUse(user, bit, useBeforeDefBits.test(bit));
617623
}
618624

619625
addInterestingUser(user, bits, lifetimeEnding);
@@ -798,74 +804,46 @@ void FieldSensitivePrunedLiveRange<LivenessWithDefs>::computeBoundary(
798804
}
799805
}
800806

807+
bool FieldSensitiveMultiDefPrunedLiveRange::isUserBeforeDef(
808+
SILInstruction *user, unsigned element) const {
809+
auto *block = user->getParent();
810+
if (!isDefBlock(block, element))
811+
return false;
812+
813+
if (llvm::any_of(block->getArguments(), [this, element](SILArgument *arg) {
814+
return isDef(arg, element);
815+
})) {
816+
return false;
817+
}
818+
819+
auto *current = user;
820+
while (true) {
821+
// If user is also a def, then the use is considered before the def.
822+
current = current->getPreviousInstruction();
823+
if (!current)
824+
return true;
825+
826+
if (isDef(current, element))
827+
return false;
828+
}
829+
}
830+
801831
template <typename LivenessWithDefs>
802832
void FieldSensitivePrunedLiveRange<LivenessWithDefs>::updateForUse(
803833
SILInstruction *user, TypeTreeLeafTypeRange range, bool lifetimeEnding) {
804-
PRUNED_LIVENESS_LOG(
805-
llvm::dbgs()
806-
<< "Begin FieldSensitivePrunedLiveRange<LivenessWithDefs>::updateForUse "
807-
"for: "
808-
<< *user);
809-
PRUNED_LIVENESS_LOG(
810-
llvm::dbgs() << "Looking for def instruction earlier in the block!\n");
811-
812-
auto *parentBlock = user->getParent();
813-
for (auto ii = std::next(user->getReverseIterator()),
814-
ie = parentBlock->rend();
815-
ii != ie; ++ii) {
816-
// If we find the def, just mark this instruction as being an interesting
817-
// instruction.
818-
if (asImpl().isDef(&*ii, range)) {
819-
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Found def: " << *ii);
820-
PRUNED_LIVENESS_LOG(llvm::dbgs()
821-
<< " Marking inst as interesting user and returning!\n");
822-
addInterestingUser(user, range, lifetimeEnding);
823-
return;
824-
}
825-
}
826-
827-
// Otherwise, just delegate to our parent class's update for use. This will
828-
// update liveness for our predecessor blocks and add this instruction as an
829-
// interesting user.
830-
PRUNED_LIVENESS_LOG(llvm::dbgs() << "No defs found! Delegating to "
831-
"FieldSensitivePrunedLiveness::updateForUse.\n");
832-
FieldSensitivePrunedLiveness::updateForUse(user, range, lifetimeEnding);
834+
SmallBitVector useBeforeDefBits(getNumSubElements());
835+
asImpl().isUserBeforeDef(user, range.getRange(), useBeforeDefBits);
836+
FieldSensitivePrunedLiveness::updateForUse(user, range, lifetimeEnding,
837+
useBeforeDefBits);
833838
}
834839

835840
template <typename LivenessWithDefs>
836841
void FieldSensitivePrunedLiveRange<LivenessWithDefs>::updateForUse(
837842
SILInstruction *user, SmallBitVector const &bits, bool lifetimeEnding) {
838-
PRUNED_LIVENESS_LOG(
839-
llvm::dbgs()
840-
<< "Begin FieldSensitivePrunedLiveRange<LivenessWithDefs>::updateForUse "
841-
"for: "
842-
<< *user);
843-
PRUNED_LIVENESS_LOG(llvm::dbgs()
844-
<< "Looking for def instruction earlier in the block!\n");
845-
846-
auto *parentBlock = user->getParent();
847-
for (auto ii = std::next(user->getReverseIterator()),
848-
ie = parentBlock->rend();
849-
ii != ie; ++ii) {
850-
// If we find the def, just mark this instruction as being an interesting
851-
// instruction.
852-
if (asImpl().isDef(&*ii, bits)) {
853-
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Found def: " << *ii);
854-
PRUNED_LIVENESS_LOG(
855-
llvm::dbgs()
856-
<< " Marking inst as interesting user and returning!\n");
857-
addInterestingUser(user, bits, lifetimeEnding);
858-
return;
859-
}
860-
}
861-
862-
// Otherwise, just delegate to our parent class's update for use. This will
863-
// update liveness for our predecessor blocks and add this instruction as an
864-
// interesting user.
865-
PRUNED_LIVENESS_LOG(llvm::dbgs()
866-
<< "No defs found! Delegating to "
867-
"FieldSensitivePrunedLiveness::updateForUse.\n");
868-
FieldSensitivePrunedLiveness::updateForUse(user, bits, lifetimeEnding);
843+
SmallBitVector useBeforeDefBits(getNumSubElements());
844+
asImpl().isUserBeforeDef(user, bits.set_bits(), useBeforeDefBits);
845+
FieldSensitivePrunedLiveness::updateForUse(user, bits, lifetimeEnding,
846+
useBeforeDefBits);
869847
}
870848

871849
//===----------------------------------------------------------------------===//

test/Interpreter/moveonly_loop.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %target-run-simple-swift(-Xfrontend -sil-verify-all) | %FileCheck %s
2+
// RUN: %target-run-simple-swift(-O -Xfrontend -sil-verify-all) | %FileCheck %s
3+
4+
// REQUIRES: executable_test
5+
6+
struct S: ~Copyable {
7+
let s: String
8+
init(_ s: String) { self.s = s }
9+
deinit { print("deiniting \(s)") }
10+
}
11+
12+
func use(_ s: borrowing S) {
13+
print("using: \(s.s)")
14+
}
15+
16+
@_silgen_name("f")
17+
func f(_ c: consuming S) {
18+
repeat {
19+
use(c)
20+
c = S("2")
21+
} while false
22+
}
23+
24+
func doit() {
25+
let s = S("1")
26+
f(s)
27+
}
28+
29+
// CHECK: using: 1
30+
// CHECK-NEXT: deiniting 1
31+
// CHECK-NEXT: deiniting 2
32+
doit()

test/SILOptimizer/discard_checking.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -473,14 +473,11 @@ struct Basics: ~Copyable {
473473
}
474474

475475
consuming func reinitAfterDiscard3_bad(_ c: Color) throws {
476-
// expected-error@-1 {{must consume 'self' before exiting method that discards self}}
477-
// FIXME: ^ this error is related to rdar://110239087
478-
479476
repeat {
480477
self = Basics() // expected-error {{cannot reinitialize 'self' after 'discard self'}}
481478
discard self // expected-note 2{{discarded self here}}
482479
} while false
483-
}
480+
} // expected-error {{must consume 'self' before exiting method that discards self}}
484481

485482
consuming func reinitAfterDiscard3_ok(_ c: Color) throws {
486483
self = Basics()

test/SILOptimizer/field_sensitive_liverange_unit.sil

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -64,28 +64,11 @@ bb4:
6464
// CHECK: MultiDef lifetime analysis:
6565
// CHECK: def in range [0, 1) instruction: store %{{.*}} to [init] [[ADDR:%.*]] : $*C
6666
// CHECK: def in range [0, 1) instruction: store %0 to [init] [[ADDR]] : $*C
67-
68-
// FIXME: rdar://111118843 : bb0 is live-out, but FieldSensitivePL incorrectly
69-
// determines it to be LiveWithin because of its mishandling of
70-
// uses-before-defs.
71-
//
72-
// The following HECK lines are the correct CHECK lines. The subsequent
73-
// CHECK lines capture the current erroneous behavior.
74-
// HECK: bb0: LiveOut
75-
// HECK: bb1: LiveWithin
76-
// HECK: last user: %{{.*}} = load [copy] [[ADDR]] : $*C
77-
// HECK: boundary edge: bb2
78-
// HECK: dead def: store %0 to [init] %1 : $*C
79-
80-
// FIXME: bb0 is not LiveWithin, it is LiveOut
81-
// CHECK: bb0: LiveWithin,
82-
// CHECK: bb1: LiveWithin,
83-
// CHECK: last user: %{{.*}} = load [copy] [[ADDR]] : $*C
84-
// FIXME: the store of the copy is not a dead-def, it is used in bb1, by the
85-
// only user added.
86-
// CHECK: dead def: store %2 to [init] %1 : $*C
87-
// CHECK: dead def: store %0 to [init] %1 : $*C
88-
67+
// CHECK: bb0: LiveOut
68+
// CHECK: bb1: LiveWithin
69+
// CHECK: last user: %{{.*}} = load [copy] [[ADDR]] : $*C
70+
// CHECK: boundary edge: bb2
71+
// CHECK: dead def: store %0 to [init] %1 : $*C
8972
// CHECK-LABEL: end running test 1 of 1 on testMultiDefUseAddressReinit
9073
sil [ossa] @testMultiDefUseAddressReinit : $@convention(thin) (@owned C) -> () {
9174
bb0(%0: @owned $C):

0 commit comments

Comments
 (0)