Skip to content

Commit 7c452ca

Browse files
committed
[pruned-liveness] Implement FieldSensitiveAddressPrunedLiveness.
This is the same algorithm as pruned liveness but assumes that one is tracking liveness from an address and uses the same scheme as DI to track liveness as a bit vector.
1 parent d072191 commit 7c452ca

File tree

2 files changed

+529
-1
lines changed

2 files changed

+529
-1
lines changed

include/swift/SIL/PrunedLiveness.h

Lines changed: 287 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@
9494
#ifndef SWIFT_SILOPTIMIZER_UTILS_PRUNEDLIVENESS_H
9595
#define SWIFT_SILOPTIMIZER_UTILS_PRUNEDLIVENESS_H
9696

97+
#include "swift/AST/TypeExpansionContext.h"
9798
#include "swift/SIL/SILBasicBlock.h"
99+
#include "swift/SIL/SILFunction.h"
98100
#include "llvm/ADT/MapVector.h"
99101
#include "llvm/ADT/PointerIntPair.h"
100102
#include "llvm/ADT/SmallVector.h"
@@ -236,6 +238,8 @@ class PrunedLiveBlocks {
236238
assert(!discoveredBlocks || discoveredBlocks->empty());
237239
}
238240

241+
unsigned getNumBitsToTrack() const { return numBitsToTrack; }
242+
239243
bool empty() const { return liveBlocks.empty(); }
240244

241245
void clear() {
@@ -290,7 +294,6 @@ class PrunedLiveBlocks {
290294
return;
291295
}
292296

293-
assert(liveBlockIter->second.size() == 1);
294297
liveBlockIter->second.getLiveness(startBitNo, endBitNo, foundLivenessInfo);
295298
}
296299

@@ -553,6 +556,289 @@ struct PrunedLivenessBoundary {
553556
ArrayRef<SILBasicBlock *> postDomBlocks);
554557
};
555558

559+
/// Given a type T and a descendent field F in T's type tree, then the
560+
/// sub-element number of F is the first leaf element of the type tree in its
561+
/// linearized representation.
562+
struct SubElementNumber {
563+
unsigned number;
564+
565+
SubElementNumber(unsigned number) : number(number) {}
566+
567+
/// Given an arbitrary projection \p projectionFromRoot from the \p
568+
/// rootAddress, compute the sub element number for that \p SILValue. The sub
569+
/// element number of a type T is always the index of its first leaf node
570+
/// descendent in the type tree.
571+
///
572+
/// DISCUSSION: This works for non-leaf types in the type tree as well as
573+
/// normal leaf elements. It is the index of the first leaf element that is a
574+
/// sub element of the root SILType that this projection will effect. The rest
575+
/// of the elements effected can be found by computing the number of leaf sub
576+
/// elements of \p projectionFromRoot's type and adding this to the result of
577+
/// this function.
578+
///
579+
/// \returns None if we didn't know how to compute sub-element for this
580+
/// projection.
581+
static Optional<SubElementNumber> compute(SILValue projectionFromRoot,
582+
SILValue root);
583+
584+
operator unsigned() const { return number; }
585+
};
586+
587+
/// Given a type T, this is the number of leaf field types in T's type tree. A
588+
/// leaf field type is a descendent field of T that does not have any
589+
/// descendent's itself.
590+
struct TypeSubElementCount {
591+
unsigned number;
592+
593+
TypeSubElementCount(unsigned number) : number(number) {}
594+
595+
/// Given a type \p type, compute the total number of leaf sub-elements of \p
596+
/// type in the type tree.
597+
///
598+
/// Some interesting properties of this computation:
599+
///
600+
/// 1. When applied to the root type, this equals the total number of bits of
601+
/// liveness that we track.
602+
///
603+
/// 2. When applied to a field type F of the type tree for a type T,
604+
/// computeNumLeafSubElements(F) when added to F's start sub element number
605+
/// will go to the next sibling node in the type tree, walking up the tree and
606+
/// attempting to find siblings if no further siblings exist.
607+
TypeSubElementCount(SILType type, SILModule &mod,
608+
TypeExpansionContext context);
609+
610+
TypeSubElementCount(SILValue value)
611+
: TypeSubElementCount(value->getType(), *value->getModule(),
612+
TypeExpansionContext(*value->getFunction())) {}
613+
614+
operator unsigned() const { return number; }
615+
};
616+
617+
/// A span of leaf elements in the sub-element break down of the linearization
618+
/// of the type tree of a type T.
619+
struct TypeTreeLeafTypeRange {
620+
SubElementNumber startEltOffset;
621+
SubElementNumber endEltOffset;
622+
623+
/// The leaf type range for the entire type tree.
624+
TypeTreeLeafTypeRange(SILValue rootAddress)
625+
: startEltOffset(0), endEltOffset(TypeSubElementCount(rootAddress)) {}
626+
627+
/// The leaf type sub-range of the type tree of \p rootAddress, consisting of
628+
/// \p projectedAddress and all of \p projectedAddress's descendent fields in
629+
/// the type tree.
630+
TypeTreeLeafTypeRange(SILValue projectedAddress, SILValue rootAddress)
631+
: startEltOffset(
632+
*SubElementNumber::compute(projectedAddress, rootAddress)),
633+
endEltOffset(startEltOffset + TypeSubElementCount(projectedAddress)) {}
634+
635+
/// Is the given leaf type specified by \p singleLeafElementNumber apart of
636+
/// our \p range of leaf type values in the our larger type.
637+
bool contains(SubElementNumber singleLeafElementNumber) const {
638+
return startEltOffset <= singleLeafElementNumber &&
639+
singleLeafElementNumber < endEltOffset;
640+
}
641+
642+
/// Returns true if either of this overlaps at all with the given range.
643+
bool contains(TypeTreeLeafTypeRange range) const {
644+
if (startEltOffset <= range.startEltOffset &&
645+
range.startEltOffset < endEltOffset)
646+
return true;
647+
648+
// If our start and end offsets, our extent is only 1 and we know that our
649+
// value
650+
unsigned rangeLastElt = range.endEltOffset - 1;
651+
if (range.startEltOffset == rangeLastElt)
652+
return false;
653+
654+
// Othrwise, see if endEltOffset - 1 is within the range.
655+
return startEltOffset <= rangeLastElt && rangeLastElt < endEltOffset;
656+
}
657+
};
658+
659+
/// This is exactly like pruned liveness except that instead of tracking a
660+
/// single bit of liveness, it tracks multiple bits of liveness for leaf type
661+
/// tree nodes of an allocation one is calculating pruned liveness for.
662+
///
663+
/// DISCUSSION: One can view a type T as a tree with recursively each field F of
664+
/// the type T being a child of T in the tree. We say recursively since the tree
665+
/// unfolds for F and its children as well.
666+
class FieldSensitiveAddressPrunedLiveness {
667+
PrunedLiveBlocks liveBlocks;
668+
669+
struct InterestingUser {
670+
TypeTreeLeafTypeRange subEltSpan;
671+
bool isConsuming;
672+
673+
InterestingUser(TypeTreeLeafTypeRange subEltSpan, bool isConsuming)
674+
: subEltSpan(subEltSpan), isConsuming(isConsuming) {}
675+
676+
InterestingUser &operator&=(bool otherValue) {
677+
isConsuming &= otherValue;
678+
return *this;
679+
}
680+
};
681+
682+
/// Map all "interesting" user instructions in this def's live range to a pair
683+
/// consisting of the SILValue that it uses and a flag indicating whether they
684+
/// must end the lifetime.
685+
///
686+
/// Lifetime-ending users are always on the boundary so are always
687+
/// interesting.
688+
///
689+
/// Non-lifetime-ending uses within a LiveWithin block are interesting because
690+
/// they may be the last use in the block.
691+
///
692+
/// Non-lifetime-ending within a LiveOut block are uninteresting.
693+
llvm::SmallMapVector<SILInstruction *, InterestingUser, 8> users;
694+
695+
/// The root address of our type tree.
696+
SILValue rootAddress;
697+
698+
public:
699+
FieldSensitiveAddressPrunedLiveness(
700+
SILFunction *fn, SILValue rootValue,
701+
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
702+
: liveBlocks(TypeSubElementCount(rootValue), discoveredBlocks),
703+
rootAddress(rootValue) {}
704+
705+
bool empty() const {
706+
assert(!liveBlocks.empty() || users.empty());
707+
return liveBlocks.empty();
708+
}
709+
710+
void clear() {
711+
liveBlocks.clear();
712+
users.clear();
713+
}
714+
715+
SILValue getRootAddress() const { return rootAddress; }
716+
717+
unsigned numLiveBlocks() const { return liveBlocks.numLiveBlocks(); }
718+
719+
/// If the constructor was provided with a vector to populate, then this
720+
/// returns the list of all live blocks with no duplicates.
721+
ArrayRef<SILBasicBlock *> getDiscoveredBlocks() const {
722+
return liveBlocks.getDiscoveredBlocks();
723+
}
724+
725+
using UserRange =
726+
iterator_range<const std::pair<SILInstruction *, InterestingUser> *>;
727+
UserRange getAllUsers() const {
728+
return llvm::make_range(users.begin(), users.end());
729+
}
730+
731+
using UserBlockRange = TransformRange<
732+
UserRange, function_ref<SILBasicBlock *(
733+
const std::pair<SILInstruction *, InterestingUser> &)>>;
734+
UserBlockRange getAllUserBlocks() const {
735+
function_ref<SILBasicBlock *(
736+
const std::pair<SILInstruction *, InterestingUser> &)>
737+
op;
738+
op = [](const std::pair<SILInstruction *, InterestingUser> &pair)
739+
-> SILBasicBlock * { return pair.first->getParent(); };
740+
return UserBlockRange(getAllUsers(), op);
741+
}
742+
743+
void initializeDefBlock(SILBasicBlock *defBB, TypeTreeLeafTypeRange span) {
744+
liveBlocks.initializeDefBlock(defBB, span.startEltOffset,
745+
span.endEltOffset);
746+
}
747+
748+
/// For flexibility, \p lifetimeEnding is provided by the
749+
/// caller. PrunedLiveness makes no assumptions about the def-use
750+
/// relationships that generate liveness. For example, use->isLifetimeEnding()
751+
/// cannot distinguish the end of the borrow scope that defines this extended
752+
/// live range vs. a nested borrow scope within the extended live range.
753+
///
754+
/// Also for flexibility, \p affectedAddress must be a derived projection from
755+
/// the base that \p user is affecting.
756+
void updateForUse(SILInstruction *user, TypeTreeLeafTypeRange span,
757+
bool lifetimeEnding);
758+
759+
void getBlockLiveness(
760+
SILBasicBlock *bb, TypeTreeLeafTypeRange span,
761+
SmallVectorImpl<PrunedLiveBlocks::IsLive> &resultingFoundLiveness) const {
762+
liveBlocks.getBlockLiveness(bb, span.startEltOffset, span.endEltOffset,
763+
resultingFoundLiveness);
764+
}
765+
766+
/// Return the liveness for this specific sub-element of our root value.
767+
PrunedLiveBlocks::IsLive getBlockLiveness(SILBasicBlock *bb,
768+
unsigned subElementNumber) const {
769+
SmallVector<PrunedLiveBlocks::IsLive, 1> isLive;
770+
liveBlocks.getBlockLiveness(bb, subElementNumber, subElementNumber + 1,
771+
isLive);
772+
return isLive[0];
773+
}
774+
775+
void getBlockLiveness(
776+
SILBasicBlock *bb,
777+
SmallVectorImpl<PrunedLiveBlocks::IsLive> &foundLiveness) const {
778+
liveBlocks.getBlockLiveness(bb, 0, liveBlocks.getNumBitsToTrack(),
779+
foundLiveness);
780+
}
781+
782+
enum IsInterestingUser { NonUser, NonLifetimeEndingUse, LifetimeEndingUse };
783+
784+
/// Return a result indicating whether the given user was identified as an
785+
/// interesting use of the current def and whether it ends the lifetime.
786+
std::pair<IsInterestingUser, Optional<TypeTreeLeafTypeRange>>
787+
isInterestingUser(SILInstruction *user) const {
788+
auto useIter = users.find(user);
789+
if (useIter == users.end())
790+
return {NonUser, None};
791+
auto isInteresting =
792+
useIter->second.isConsuming ? LifetimeEndingUse : NonLifetimeEndingUse;
793+
return {isInteresting, useIter->second.subEltSpan};
794+
}
795+
796+
unsigned getNumSubElements() const { return liveBlocks.getNumBitsToTrack(); }
797+
798+
/// Return true if \p inst occurs before the liveness boundary. Used when the
799+
/// client already knows that inst occurs after the start of liveness.
800+
void isWithinBoundary(SILInstruction *inst, SmallBitVector &outVector) const;
801+
};
802+
803+
/// Record the last use points and CFG edges that form the boundary of
804+
/// FieldSensitiveAddressPrunedLiveness. It does this on a per type tree leaf
805+
/// node basis.
806+
struct FieldSensitiveAddressPrunedLivenessBoundary {
807+
/// The list of last users and an associated SILValue that is the address that
808+
/// is being used. The address can be used to determine the start sub element
809+
/// number of the user in the type tree and the end sub element number.
810+
///
811+
/// TODO (MG): If we don't eventually need to store the SILValue here (I am
812+
/// not sure yet...), just store a tuple with the start/end sub element
813+
/// number.
814+
SmallVector<std::tuple<SILInstruction *, TypeTreeLeafTypeRange>, 8> lastUsers;
815+
816+
/// Blocks where the value was live out but had a successor that was dead.
817+
SmallVector<SILBasicBlock *, 8> boundaryEdges;
818+
819+
void clear() {
820+
lastUsers.clear();
821+
boundaryEdges.clear();
822+
}
823+
824+
/// Compute the boundary from the blocks discovered during liveness analysis.
825+
///
826+
/// Precondition: \p liveness.getDiscoveredBlocks() is a valid list of all
827+
/// live blocks with no duplicates.
828+
///
829+
/// The computed boundary will completely post-dominate, including dead end
830+
/// paths. The client should query DeadEndBlocks to ignore those dead end
831+
/// paths.
832+
void compute(const FieldSensitiveAddressPrunedLiveness &liveness);
833+
834+
private:
835+
void
836+
findLastUserInBlock(SILBasicBlock *bb,
837+
FieldSensitiveAddressPrunedLivenessBoundary &boundary,
838+
const FieldSensitiveAddressPrunedLiveness &liveness,
839+
unsigned subElementNumber);
840+
};
841+
556842
} // namespace swift
557843

558844
#endif

0 commit comments

Comments
 (0)