Skip to content

Commit 197a4a3

Browse files
Merge pull request #68756 from nate-chandler/nfc/20230925/1/distinguish-non-users
[PrunedLiveness] Add extendToNonUse.
2 parents 22ddfa1 + 05bec97 commit 197a4a3

17 files changed

+877
-192
lines changed

include/swift/SIL/FieldSensitivePrunedLiveness.h

Lines changed: 74 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -375,9 +375,12 @@ struct TypeTreeLeafTypeRange {
375375

376376
/// Sets each bit in \p bits corresponding to an element of this range.
377377
void setBits(SmallBitVector &bits) const {
378-
for (auto element : getRange()) {
379-
bits.set(element);
380-
}
378+
bits.set(startEltOffset, endEltOffset);
379+
}
380+
381+
/// Resets each bit in \p bits corresponding to an element of this range.
382+
void resetBits(SmallBitVector &bits) const {
383+
bits.reset(startEltOffset, endEltOffset);
381384
}
382385

383386
IntRange<unsigned> getRange() const {
@@ -694,6 +697,15 @@ class FieldSensitivePrunedLiveness {
694697
enum IsInterestingUser { NonUser, NonLifetimeEndingUse, LifetimeEndingUse };
695698

696699
struct InterestingUser {
700+
// Together these bit vectors encode four states per field:
701+
// +---------------+----------------+---------------+
702+
// - | liveBits[bit] | consumingBits] | state |
703+
// +---------------+----------------+---------------+
704+
// | 0 | 0 | dead |
705+
// | 0 | 1 | non-use |
706+
// | 1 | 0 | non-consuming |
707+
// | 1 | 1 | consuming |
708+
// +---------------+----------------+---------------+
697709
SmallBitVector liveBits;
698710
SmallBitVector consumingBits;
699711

@@ -708,18 +720,33 @@ class FieldSensitivePrunedLiveness {
708720

709721
/// Record that the instruction uses the bits of the value in \p range.
710722
void addUses(TypeTreeLeafTypeRange range, bool lifetimeEnding) {
711-
range.setBits(liveBits);
712-
if (lifetimeEnding) {
713-
range.setBits(consumingBits);
714-
}
723+
SmallBitVector bits(liveBits.size());
724+
range.setBits(bits);
725+
addUses(bits, lifetimeEnding);
715726
}
716727

717728
/// Record that the instruction uses the bits in \p bits.
718729
void addUses(SmallBitVector const &bits, bool lifetimeEnding) {
719-
liveBits |= bits;
720730
if (lifetimeEnding) {
721-
consumingBits |= bits;
731+
consumingBits |= bits & ~liveBits;
732+
} else {
733+
consumingBits &= ~bits;
722734
}
735+
liveBits |= bits;
736+
}
737+
738+
/// Extend liveness at the bits in the specified \p range without
739+
/// overriding whether the lifetimes of those bits end.
740+
void extendToNonUse(TypeTreeLeafTypeRange range) {
741+
SmallBitVector bits(liveBits.size());
742+
range.setBits(bits);
743+
extendToNonUse(bits);
744+
}
745+
746+
/// Extend liveness at the specified \p bits without overriding whether the
747+
/// lifetimes of those bits end.
748+
void extendToNonUse(SmallBitVector const &bits) {
749+
consumingBits |= bits & ~liveBits;
723750
}
724751

725752
/// Populates the provided vector with contiguous ranges of bits which are
@@ -755,10 +782,18 @@ class FieldSensitivePrunedLiveness {
755782
}
756783

757784
IsInterestingUser isInterestingUser(unsigned element) const {
758-
if (!liveBits.test(element))
785+
auto isLive = liveBits.test(element);
786+
auto isConsuming = consumingBits.test(element);
787+
if (!isLive && !isConsuming) {
759788
return NonUser;
760-
return consumingBits.test(element) ? LifetimeEndingUse
761-
: NonLifetimeEndingUse;
789+
} else if (!isLive && isConsuming) {
790+
return NonLifetimeEndingUse;
791+
} else if (isLive && isConsuming) {
792+
return LifetimeEndingUse;
793+
} else if (isLive && !isConsuming) {
794+
return NonLifetimeEndingUse;
795+
}
796+
llvm_unreachable("covered conditions");
762797
}
763798
};
764799

@@ -875,6 +910,16 @@ class FieldSensitivePrunedLiveness {
875910
bool lifetimeEnding,
876911
SmallBitVector const &useBeforeDefBits);
877912

913+
/// Adds \p user which doesn't use the def to liveness.
914+
///
915+
/// Different from calling updateForUse because it never overrides the value
916+
/// \p lifetimeEnding stored for \p inst.
917+
void extendToNonUse(SILInstruction *user, TypeTreeLeafTypeRange span,
918+
SmallBitVector const &useBeforeDefBits);
919+
920+
void extendToNonUse(SILInstruction *user, SmallBitVector const &bits,
921+
SmallBitVector const &useBeforeDefBits);
922+
878923
void getBlockLiveness(SILBasicBlock *bb, TypeTreeLeafTypeRange span,
879924
SmallVectorImpl<FieldSensitivePrunedLiveBlocks::IsLive>
880925
&resultingFoundLiveness) const {
@@ -943,7 +988,7 @@ class FieldSensitivePrunedLiveness {
943988

944989
unsigned getNumSubElements() const { return liveBlocks.getNumBitsToTrack(); }
945990

946-
void print(llvm::raw_ostream &os) const { liveBlocks.print(os); }
991+
void print(llvm::raw_ostream &os) const;
947992
SWIFT_DEBUG_DUMP { print(llvm::dbgs()); }
948993

949994
protected:
@@ -971,6 +1016,14 @@ class FieldSensitivePrunedLiveness {
9711016
bool lifetimeEnding) {
9721017
getOrCreateInterestingUser(user).addUses(bits, lifetimeEnding);
9731018
}
1019+
1020+
void extendToNonUse(SILInstruction *user, TypeTreeLeafTypeRange range) {
1021+
getOrCreateInterestingUser(user).extendToNonUse(range);
1022+
}
1023+
1024+
void extendToNonUse(SILInstruction *user, SmallBitVector const &bits) {
1025+
getOrCreateInterestingUser(user).extendToNonUse(bits);
1026+
}
9741027
};
9751028

9761029
/// Record the last use points and CFG edges that form the boundary of
@@ -1089,6 +1142,14 @@ class FieldSensitivePrunedLiveRange : public FieldSensitivePrunedLiveness {
10891142
void updateForUse(SILInstruction *user, SmallBitVector const &bits,
10901143
bool lifetimeEnding);
10911144

1145+
/// Customize extendToNonUse for FieldSensitivePrunedLiveness to consider
1146+
/// defs as kills.
1147+
void extendToNonUse(SILInstruction *user, TypeTreeLeafTypeRange span);
1148+
1149+
/// Customize extendToNonUse for FieldSensitivePrunedLiveness to consider
1150+
/// defs as kills.
1151+
void extendToNonUse(SILInstruction *user, SmallBitVector const &bits);
1152+
10921153
/// Compute the boundary from the blocks discovered during liveness analysis.
10931154
///
10941155
/// Precondition: \p liveness.getDiscoveredBlocks() is a valid list of all

include/swift/SIL/PrunedLiveness.h

Lines changed: 84 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,61 @@ struct LiveRangeSummary {
353353
/// necessarily include liveness up to destroy_value or end_borrow
354354
/// instructions.
355355
class PrunedLiveness {
356+
public:
357+
/// A tristate describing how an instruction uses the value:
358+
/// - non-ending: the value's lifetime does not end at the instruction;
359+
/// results from updateForUse(, lifetimeEnding: false)
360+
/// - ending: the value's lifetime ends at the instruction; results from
361+
/// updateForUse(, lifetimeEnding: true)
362+
/// - non-use: the instruction doesn't use the value but liveness was extended
363+
/// to it; results from extendToNonUse
364+
///
365+
/// If an instruction is added to liveness with multiple LifetimeEnding
366+
/// instances, the stored instance needs to be updated appropriately, taking
367+
/// into account the instances that were already seen.
368+
///
369+
/// For example, if Nonuse is seen after Ending, it is ignored: that the
370+
/// instruction ends the lifetime takes priority: the instruction doesn't end
371+
/// the lifetime of the value any less because liveness was extended to it.
372+
///
373+
/// Similarly, if Ending is seen after NonEnding, it is ignored: that the
374+
/// instruction ends the lifetime of a copy of the value can't change the fact
375+
/// that liveness already extends _beyond_ the instruction because it was
376+
/// already recognized as a non-ending use.
377+
///
378+
/// This relationship of "overriding" is captured by the order of the cases in
379+
/// LifetimeEnding::Value and which case overrides another is computed by
380+
/// taking the meet--i.e. the lesser of the two cases overrides.
381+
///
382+
/// Note: Taking the meet may not be appropriate for branch instructions
383+
/// which may need to be recognized as lifetime-ending when they have
384+
/// as uses both a reborrow and a guaranteed phi.
385+
struct LifetimeEnding {
386+
enum class Value {
387+
// The instruction doesn't consume the value.
388+
NonEnding,
389+
// The instruction consumes the value.
390+
Ending,
391+
// The instruction doesn't use the value.
392+
NonUse,
393+
};
394+
Value value;
395+
396+
LifetimeEnding(Value value) : value(value) {}
397+
explicit LifetimeEnding(bool lifetimeEnding)
398+
: value(lifetimeEnding ? Value::Ending : Value::NonEnding) {}
399+
operator Value() const { return value; }
400+
LifetimeEnding meet(LifetimeEnding const other) const {
401+
return value < other.value ? *this : other;
402+
}
403+
void meetInPlace(LifetimeEnding const other) { *this = meet(other); }
404+
bool isEnding() const { return value == Value::Ending; }
405+
406+
static LifetimeEnding NonUse() { return {Value::NonUse}; };
407+
static LifetimeEnding Ending() { return {Value::Ending}; };
408+
static LifetimeEnding NonEnding() { return {Value::NonEnding}; };
409+
};
410+
356411
protected:
357412
PrunedLiveBlocks liveBlocks;
358413

@@ -365,7 +420,7 @@ class PrunedLiveness {
365420
// they may be the last use in the block.
366421
//
367422
// Non-lifetime-ending within a LiveOut block are uninteresting.
368-
llvm::SmallMapVector<SILInstruction *, bool, 8> users;
423+
llvm::SmallMapVector<SILInstruction *, LifetimeEnding, 8> users;
369424

370425
public:
371426
PrunedLiveness(SILFunction *function,
@@ -411,11 +466,12 @@ class PrunedLiveness {
411466
auto useIter = users.find(user);
412467
if (useIter == users.end())
413468
return NonUser;
414-
return useIter->second ? LifetimeEndingUse : NonLifetimeEndingUse;
469+
return useIter->second == LifetimeEnding::Ending() ? LifetimeEndingUse
470+
: NonLifetimeEndingUse;
415471
}
416472

417473
using ConstUserRange =
418-
iterator_range<const std::pair<SILInstruction *, bool> *>;
474+
iterator_range<const std::pair<SILInstruction *, LifetimeEnding> *>;
419475
ConstUserRange getAllUsers() const {
420476
return llvm::make_range(users.begin(), users.end());
421477
}
@@ -425,49 +481,53 @@ class PrunedLiveness {
425481
/// IDE.
426482
struct RangeIterationHelpers {
427483
struct MapFunctor {
428-
SILInstruction *
429-
operator()(const std::pair<SILInstruction *, bool> &pair) const {
484+
SILInstruction *operator()(
485+
const std::pair<SILInstruction *, LifetimeEnding> &pair) const {
430486
// Strip off the const to ease use with other APIs.
431487
return const_cast<SILInstruction *>(pair.first);
432488
}
433489
};
434490

435-
struct LifetimeEnding {
491+
struct IsLifetimeEnding {
436492
struct FilterFunctor {
437-
bool operator()(const std::pair<SILInstruction *, bool> &pair) const {
438-
return pair.second;
493+
bool operator()(
494+
const std::pair<SILInstruction *, LifetimeEnding> &pair) const {
495+
return pair.second.isEnding();
439496
}
440497
};
441498

442499
using MapFilterIter = llvm::mapped_iterator<
443-
llvm::filter_iterator<const std::pair<SILInstruction *, bool> *,
444-
FilterFunctor>,
500+
llvm::filter_iterator<
501+
const std::pair<SILInstruction *, LifetimeEnding> *,
502+
FilterFunctor>,
445503
MapFunctor>;
446504
};
447505

448506
struct NonLifetimeEnding {
449507
struct FilterFunctor {
450-
bool operator()(const std::pair<SILInstruction *, bool> &pair) const {
451-
return !pair.second;
508+
bool operator()(
509+
const std::pair<SILInstruction *, LifetimeEnding> &pair) const {
510+
return !pair.second.isEnding();
452511
}
453512
};
454513

455514
using MapFilterIter = llvm::mapped_iterator<
456-
llvm::filter_iterator<const std::pair<SILInstruction *, bool> *,
457-
FilterFunctor>,
515+
llvm::filter_iterator<
516+
const std::pair<SILInstruction *, LifetimeEnding> *,
517+
FilterFunctor>,
458518
MapFunctor>;
459519
};
460520
};
461521
using LifetimeEndingUserRange = llvm::iterator_range<
462-
RangeIterationHelpers::LifetimeEnding::MapFilterIter>;
522+
RangeIterationHelpers::IsLifetimeEnding::MapFilterIter>;
463523

464524
/// Return a range consisting of the current set of consuming users fed into
465525
/// this PrunedLiveness instance.
466526
LifetimeEndingUserRange getLifetimeEndingUsers() const {
467527
return map_range(
468528
llvm::make_filter_range(
469529
getAllUsers(),
470-
RangeIterationHelpers::LifetimeEnding::FilterFunctor()),
530+
RangeIterationHelpers::IsLifetimeEnding::FilterFunctor()),
471531
RangeIterationHelpers::MapFunctor());
472532
}
473533

@@ -552,6 +612,8 @@ class PrunedLiveRange : public PrunedLiveness {
552612
ValueSet &visited,
553613
SILValue value);
554614

615+
void updateForUse(SILInstruction *user, LifetimeEnding lifetimeEnding);
616+
555617
public:
556618
/// For flexibility, \p lifetimeEnding is provided by the
557619
/// caller. PrunedLiveness makes no assumptions about the def-use
@@ -560,6 +622,12 @@ class PrunedLiveRange : public PrunedLiveness {
560622
/// live range vs. a nested borrow scope within the extended live range.
561623
void updateForUse(SILInstruction *user, bool lifetimeEnding);
562624

625+
/// Adds \p inst which doesn't use the def to liveness.
626+
///
627+
/// Different from calling updateForUse because it never overrides the value
628+
/// \p lifetimeEnding stored for \p inst.
629+
void extendToNonUse(SILInstruction *inst);
630+
563631
/// Updates the liveness for a whole borrow scope, beginning at \p op.
564632
/// Returns false if this cannot be done. This assumes that nested OSSA
565633
/// lifetimes are complete.

include/swift/SIL/SILBasicBlock.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,29 @@ inline SILFunction *SILInstruction::getFunction() const {
742742
return getParent()->getParent();
743743
}
744744

745+
inline bool SILInstruction::visitPriorInstructions(
746+
llvm::function_ref<bool(SILInstruction *)> visitor) {
747+
if (auto *previous = getPreviousInstruction()) {
748+
return visitor(previous);
749+
}
750+
for (auto *predecessor : getParent()->getPredecessorBlocks()) {
751+
if (!visitor(&predecessor->back()))
752+
return false;
753+
}
754+
return true;
755+
}
756+
inline bool SILInstruction::visitSubsequentInstructions(
757+
llvm::function_ref<bool(SILInstruction *)> visitor) {
758+
if (auto *next = getNextInstruction()) {
759+
return visitor(next);
760+
}
761+
for (auto *successor : getParent()->getSuccessorBlocks()) {
762+
if (!visitor(&successor->front()))
763+
return false;
764+
}
765+
return true;
766+
}
767+
745768
inline SILInstruction *SILInstruction::getPreviousInstruction() {
746769
auto pos = getIterator();
747770
return pos == getParent()->begin() ? nullptr : &*std::prev(pos);

include/swift/SIL/SILInstruction.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,24 @@ class SILInstruction : public llvm::ilist_node<SILInstruction> {
517517
/// instruction in its block.
518518
SILInstruction *getNextInstruction();
519519

520+
/// Calls \p visitor with each instruction that is immediately prior. Returns
521+
/// false and stops visiting if \p visitor returns false.
522+
///
523+
/// If \p this is is the first instruction in a block, then \p visitor is
524+
/// called with the back of each predecessor. In particular if \p this is
525+
/// the first instruction of the entry block, \p visitor is never called.
526+
bool
527+
visitPriorInstructions(llvm::function_ref<bool(SILInstruction *)> visitor);
528+
529+
/// Calls \p visitor with each instruction that is immediately subsequent.
530+
/// Returns false and stops visiting if \p visitor returns false.
531+
///
532+
/// If \p this is the last instruction in a block, then \p visitor is called
533+
/// with the front of each successor. In particular if \p this is the last
534+
/// instruction of a block without successors, \p visitor is never called.
535+
bool visitSubsequentInstructions(
536+
llvm::function_ref<bool(SILInstruction *)> visitor);
537+
520538
/// This method unlinks 'self' from the containing basic block and deletes it.
521539
void eraseFromParent();
522540

0 commit comments

Comments
 (0)