Skip to content

Commit f4e3292

Browse files
committed
[FieldSensitivePL] Fix vectorization.
FieldSensitivePrunedLiveness is used as a vectorization of PrunedLiveness. An instance of FSPL with N elements needs to be able to represent the same states as N instances of PL. Previously, it failed to do that in two significant ways: (1) It attempted to save space for which elements were live by using a range. This failed to account for instructions which are users of non-contiguous fields of an aggregate. apply( @owned (struct_element_addr %s, #S.f1), @owned (struct_element_addr %s, #S.f3) ) (2) It used a single bit to represent whether the instruction was consuming. This failed to account for instructions which consumed some fields and borrowed others. apply( @owned (struct_element_addr %s, #S.f1), @guaranteed (struct_element_addr %s, #S.f2) ) The fix for (1) is to use a bit vector to represent which elements are used by the instruction. The fix for (2) is to use a second bit vector to represent which elements are _consumed_ by the instruction. Adapted the move-checker to use the new representation. rdar://110909290
1 parent 934ec10 commit f4e3292

File tree

5 files changed

+319
-148
lines changed

5 files changed

+319
-148
lines changed

include/swift/SIL/FieldSensitivePrunedLiveness.h

Lines changed: 92 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,13 @@ struct TypeTreeLeafTypeRange {
358358
endEltOffset >= range.endEltOffset;
359359
}
360360

361+
/// Sets each bit in \p bits corresponding to an element of this range.
362+
void setBits(SmallBitVector &bits) {
363+
for (auto element : getRange()) {
364+
bits.set(element);
365+
}
366+
}
367+
361368
IntRange<unsigned> getRange() const {
362369
return range(startEltOffset, endEltOffset);
363370
}
@@ -666,17 +673,60 @@ class FieldSensitivePrunedLiveness {
666673
FieldSensitivePrunedLiveBlocks liveBlocks;
667674

668675
public:
676+
enum IsInterestingUser { NonUser, NonLifetimeEndingUse, LifetimeEndingUse };
677+
669678
struct InterestingUser {
670-
TypeTreeLeafTypeRange subEltSpan;
671-
bool isConsuming;
679+
SmallBitVector liveBits;
680+
SmallBitVector consumingBits;
672681

673-
InterestingUser() : subEltSpan(), isConsuming(false) {}
674-
InterestingUser(TypeTreeLeafTypeRange subEltSpan, bool isConsuming)
675-
: subEltSpan(subEltSpan), isConsuming(isConsuming) {}
682+
InterestingUser(unsigned bitCount)
683+
: liveBits(bitCount), consumingBits(bitCount) {}
676684

677-
InterestingUser &operator&=(bool otherValue) {
678-
isConsuming &= otherValue;
679-
return *this;
685+
InterestingUser(unsigned bitCount, TypeTreeLeafTypeRange range,
686+
bool lifetimeEnding)
687+
: liveBits(bitCount), consumingBits(bitCount) {
688+
addUses(range, lifetimeEnding);
689+
}
690+
691+
/// Record that the instruction uses the bits of the value in \p range.
692+
void addUses(TypeTreeLeafTypeRange range, bool lifetimeEnding) {
693+
range.setBits(liveBits);
694+
if (lifetimeEnding) {
695+
range.setBits(consumingBits);
696+
}
697+
}
698+
699+
/// Populates the provided vector with contiguous ranges of bits which are
700+
/// users of the same sort.
701+
void getContiguousRanges(
702+
SmallVectorImpl<std::pair<TypeTreeLeafTypeRange, IsInterestingUser>>
703+
&ranges) const {
704+
if (liveBits.size() == 0)
705+
return;
706+
707+
assert(ranges.empty());
708+
Optional<std::pair<unsigned, IsInterestingUser>> current = llvm::None;
709+
for (unsigned bit = 0, size = liveBits.size(); bit < size; ++bit) {
710+
auto interesting = isInterestingUser(bit);
711+
if (!current) {
712+
current = {bit, interesting};
713+
continue;
714+
}
715+
if (current->second != interesting) {
716+
ranges.push_back(
717+
{TypeTreeLeafTypeRange(current->first, bit), current->second});
718+
current = {bit, interesting};
719+
}
720+
}
721+
ranges.push_back({TypeTreeLeafTypeRange(current->first, liveBits.size()),
722+
current->second});
723+
}
724+
725+
IsInterestingUser isInterestingUser(unsigned element) const {
726+
if (!liveBits.test(element))
727+
return NonUser;
728+
return consumingBits.test(element) ? LifetimeEndingUse
729+
: NonLifetimeEndingUse;
680730
}
681731
};
682732

@@ -758,42 +808,6 @@ class FieldSensitivePrunedLiveness {
758808
return llvm::make_range(users.begin(), users.end());
759809
}
760810

761-
using LifetimeEndingUserRange = OptionalTransformRange<
762-
UserRange,
763-
function_ref<Optional<std::pair<SILInstruction *, TypeTreeLeafTypeRange>>(
764-
const std::pair<SILInstruction *, InterestingUser> &)>>;
765-
LifetimeEndingUserRange getAllLifetimeEndingUses() const {
766-
assert(isInitialized());
767-
function_ref<Optional<std::pair<SILInstruction *, TypeTreeLeafTypeRange>>(
768-
const std::pair<SILInstruction *, InterestingUser> &)>
769-
op;
770-
op = [](const std::pair<SILInstruction *, InterestingUser> &pair)
771-
-> Optional<std::pair<SILInstruction *, TypeTreeLeafTypeRange>> {
772-
if (pair.second.isConsuming)
773-
return {{pair.first, pair.second.subEltSpan}};
774-
return None;
775-
};
776-
return LifetimeEndingUserRange(getAllUsers(), op);
777-
}
778-
779-
using NonLifetimeEndingUserRange = OptionalTransformRange<
780-
UserRange,
781-
function_ref<Optional<std::pair<SILInstruction *, TypeTreeLeafTypeRange>>(
782-
const std::pair<SILInstruction *, InterestingUser> &)>>;
783-
NonLifetimeEndingUserRange getAllNonLifetimeEndingUses() const {
784-
assert(isInitialized());
785-
function_ref<Optional<std::pair<SILInstruction *, TypeTreeLeafTypeRange>>(
786-
const std::pair<SILInstruction *, InterestingUser> &)>
787-
op;
788-
op = [](const std::pair<SILInstruction *, InterestingUser> &pair)
789-
-> Optional<std::pair<SILInstruction *, TypeTreeLeafTypeRange>> {
790-
if (!pair.second.isConsuming)
791-
return {{pair.first, pair.second.subEltSpan}};
792-
return None;
793-
};
794-
return NonLifetimeEndingUserRange(getAllUsers(), op);
795-
}
796-
797811
using UserBlockRange = TransformRange<
798812
UserRange, function_ref<SILBasicBlock *(
799813
const std::pair<SILInstruction *, InterestingUser> &)>>;
@@ -848,19 +862,37 @@ class FieldSensitivePrunedLiveness {
848862
SmallBitVector &liveOutBits,
849863
SmallBitVector &deadBits) const;
850864

851-
enum IsInterestingUser { NonUser, NonLifetimeEndingUse, LifetimeEndingUse };
865+
/// If \p user has had uses recored, return a pointer to the InterestingUser
866+
/// where they've been recorded.
867+
InterestingUser const *getInterestingUser(SILInstruction *user) const {
868+
auto iter = users.find(user);
869+
if (iter == users.end())
870+
return nullptr;
871+
return &iter->second;
872+
}
852873

853-
/// Return a result indicating whether the given user was identified as an
854-
/// interesting use of the current def and whether it ends the lifetime.
855-
std::pair<IsInterestingUser, Optional<TypeTreeLeafTypeRange>>
856-
isInterestingUser(SILInstruction *user) const {
874+
/// How \p user uses the field at \p element.
875+
IsInterestingUser isInterestingUser(SILInstruction *user,
876+
unsigned element) const {
857877
assert(isInitialized());
858-
auto useIter = users.find(user);
859-
if (useIter == users.end())
860-
return {NonUser, None};
861-
auto isInteresting =
862-
useIter->second.isConsuming ? LifetimeEndingUse : NonLifetimeEndingUse;
863-
return {isInteresting, useIter->second.subEltSpan};
878+
auto *record = getInterestingUser(user);
879+
if (!record)
880+
return NonUser;
881+
return record->isInterestingUser(element);
882+
}
883+
884+
/// Whether \p user uses the fields in \p range as indicated by \p kind.
885+
bool isInterestingUserOfKind(SILInstruction *user, IsInterestingUser kind,
886+
TypeTreeLeafTypeRange range) const {
887+
auto *record = getInterestingUser(user);
888+
if (!record)
889+
return kind == IsInterestingUser::NonUser;
890+
891+
for (auto element : range.getRange()) {
892+
if (isInterestingUser(user, element) != kind)
893+
return false;
894+
}
895+
return true;
864896
}
865897

866898
unsigned getNumSubElements() const { return liveBlocks.getNumBitsToTrack(); }
@@ -886,10 +918,11 @@ class FieldSensitivePrunedLiveness {
886918
/// argument must be copied.
887919
void addInterestingUser(SILInstruction *user, TypeTreeLeafTypeRange range,
888920
bool lifetimeEnding) {
889-
auto iterAndSuccess =
890-
users.insert({user, InterestingUser(range, lifetimeEnding)});
891-
if (!iterAndSuccess.second)
892-
iterAndSuccess.first->second &= lifetimeEnding;
921+
auto iter = users.find(user);
922+
if (iter == users.end()) {
923+
iter = users.insert({user, InterestingUser(getNumSubElements())}).first;
924+
}
925+
iter->second.addUses(range, lifetimeEnding);
893926
}
894927
};
895928

lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -705,9 +705,7 @@ bool FieldSensitivePrunedLiveRange<LivenessWithDefs>::isWithinBoundary(
705705
// If we are not live and have an interesting user that maps to our bit,
706706
// mark this bit as being live again.
707707
if (!isLive) {
708-
auto interestingUser = isInterestingUser(&blockInst);
709-
bool isInteresting =
710-
interestingUser.first && interestingUser.second->contains(bit);
708+
bool isInteresting = isInterestingUser(&blockInst, bit);
711709
PRUNED_LIVENESS_LOG(llvm::dbgs()
712710
<< " Inst was dead... Is InterestingUser: "
713711
<< (isInteresting ? "true" : "false") << '\n');
@@ -838,8 +836,7 @@ void findBoundaryInNonDefBlock(SILBasicBlock *block, unsigned bitNo,
838836
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Looking for boundary in non-def block\n");
839837
for (SILInstruction &inst : llvm::reverse(*block)) {
840838
PRUNED_LIVENESS_LOG(llvm::dbgs() << "Visiting: " << inst);
841-
auto interestingUser = liveness.isInterestingUser(&inst);
842-
if (interestingUser.first && interestingUser.second->contains(bitNo)) {
839+
if (liveness.isInterestingUser(&inst, bitNo)) {
843840
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Is interesting user for this bit!\n");
844841
boundary.getLastUserBits(&inst).set(bitNo);
845842
return;
@@ -869,8 +866,7 @@ void findBoundaryInSSADefBlock(SILNode *ssaDef, unsigned bitNo,
869866
boundary.getDeadDefsBits(cast<SILNode>(&inst)).set(bitNo);
870867
return;
871868
}
872-
auto interestingUser = liveness.isInterestingUser(&inst);
873-
if (interestingUser.first && interestingUser.second->contains(bitNo)) {
869+
if (liveness.isInterestingUser(&inst, bitNo)) {
874870
PRUNED_LIVENESS_LOG(llvm::dbgs() << " Found interesting user: " << inst);
875871
boundary.getLastUserBits(&inst).set(bitNo);
876872
return;
@@ -1005,8 +1001,7 @@ void FieldSensitiveMultiDefPrunedLiveRange::findBoundariesInBlock(
10051001
PRUNED_LIVENESS_LOG(llvm::dbgs()
10061002
<< " Checking if this inst is also a last user...\n");
10071003
if (!isLive) {
1008-
auto interestingUser = isInterestingUser(&inst);
1009-
if (interestingUser.first && interestingUser.second->contains(bitNo)) {
1004+
if (isInterestingUser(&inst, bitNo)) {
10101005
PRUNED_LIVENESS_LOG(
10111006
llvm::dbgs()
10121007
<< " Was interesting user! Moving from dead -> live!\n");

0 commit comments

Comments
 (0)