Skip to content

Commit 47b72b4

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 47b72b4

File tree

5 files changed

+322
-148
lines changed

5 files changed

+322
-148
lines changed

include/swift/SIL/FieldSensitivePrunedLiveness.h

+95-59
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,59 @@ 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) : liveBits(bitCount), consumingBits(bitCount) {}
676683

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

@@ -758,42 +807,6 @@ class FieldSensitivePrunedLiveness {
758807
return llvm::make_range(users.begin(), users.end());
759808
}
760809

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-
797810
using UserBlockRange = TransformRange<
798811
UserRange, function_ref<SILBasicBlock *(
799812
const std::pair<SILInstruction *, InterestingUser> &)>>;
@@ -848,19 +861,39 @@ class FieldSensitivePrunedLiveness {
848861
SmallBitVector &liveOutBits,
849862
SmallBitVector &deadBits) const;
850863

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

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 {
873+
/// How \p user uses the field at \p element.
874+
IsInterestingUser isInterestingUser(SILInstruction *user,
875+
unsigned element) const {
857876
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};
877+
auto *record = getInterestingUser(user);
878+
if (!record)
879+
return NonUser;
880+
return record->isInterestingUser(element);
881+
}
882+
883+
/// Whether \p user uses the fields in \p range as indicated by \p kind.
884+
bool
885+
isInterestingUserOfKind(SILInstruction *user,
886+
IsInterestingUser kind,
887+
TypeTreeLeafTypeRange range) const {
888+
auto *record = getInterestingUser(user);
889+
if (!record)
890+
return kind == IsInterestingUser::NonUser;
891+
892+
for (auto element : range.getRange()) {
893+
if (isInterestingUser(user, element) != kind)
894+
return false;
895+
}
896+
return true;
864897
}
865898

866899
unsigned getNumSubElements() const { return liveBlocks.getNumBitsToTrack(); }
@@ -886,10 +919,13 @@ class FieldSensitivePrunedLiveness {
886919
/// argument must be copied.
887920
void addInterestingUser(SILInstruction *user, TypeTreeLeafTypeRange range,
888921
bool lifetimeEnding) {
889-
auto iterAndSuccess =
890-
users.insert({user, InterestingUser(range, lifetimeEnding)});
891-
if (!iterAndSuccess.second)
892-
iterAndSuccess.first->second &= lifetimeEnding;
922+
auto iter = users.find(user);
923+
if (iter == users.end()) {
924+
iter = users
925+
.insert({user, InterestingUser(getNumSubElements())})
926+
.first;
927+
}
928+
iter->second.addUses(range, lifetimeEnding);
893929
}
894930
};
895931

lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp

+4-9
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)