Skip to content

Commit 86cd960

Browse files
committed
[PrunedLiveness] Add lower detail boundary.
To enable those clients which care only about _blocks_ whose instructions or dead-defs make up the liveness boundary to avoid iterating instruction lists, enhance boundary computation with a second type (`PrunedLivenessBlockBoundary`) and migrate some methods from liveness onto the boundary types to enable dispatching on the boundary type to do only the amount of analysis that's necessary.
1 parent 84b186e commit 86cd960

File tree

2 files changed

+140
-34
lines changed

2 files changed

+140
-34
lines changed

include/swift/SIL/PrunedLiveness.h

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,26 @@ class PrunedLiveness {
554554
void dump() const;
555555
};
556556

557+
/// Recording liveness boundary at some level of detail; see concrete subclasses
558+
/// PrunedLivenessBoundary and PrunedLivenessBlockBoundary.
559+
struct AnyPrunedLivenessBoundary {
560+
virtual ~AnyPrunedLivenessBoundary() {}
561+
/// Targets whose single predecessor has at least one non-boundary successor.
562+
SmallVector<SILBasicBlock *, 8> boundaryEdges;
563+
564+
friend class SSAPrunedLiveness;
565+
friend class MultiDefPrunedLiveness;
566+
567+
private:
568+
virtual void findBoundaryInSSADefBlock(SILNode *ssaDef,
569+
const PrunedLiveness &liveness) = 0;
570+
virtual void
571+
findBoundaryInMultiDefBlock(SILBasicBlock *block, bool isLiveOut,
572+
const MultiDefPrunedLiveness &liveness) = 0;
573+
virtual void findBoundaryInNonDefBlock(SILBasicBlock *block,
574+
const PrunedLiveness &liveness) = 0;
575+
};
576+
557577
/// Record the last use points and CFG edges that form the boundary of
558578
/// PrunedLiveness.
559579
///
@@ -565,9 +585,8 @@ class PrunedLiveness {
565585
/// Each boundary edge is identified by its target block. The source of the edge
566586
/// is the target block's single predecessor which must have at least one other
567587
/// non-boundary successor.
568-
struct PrunedLivenessBoundary {
588+
struct PrunedLivenessBoundary : AnyPrunedLivenessBoundary {
569589
SmallVector<SILInstruction *, 8> lastUsers;
570-
SmallVector<SILBasicBlock *, 8> boundaryEdges;
571590
SmallVector<SILNode *, 1> deadDefs;
572591

573592
void clear() {
@@ -586,6 +605,39 @@ struct PrunedLivenessBoundary {
586605

587606
void print(llvm::raw_ostream &OS) const;
588607
void dump() const;
608+
609+
private:
610+
void findBoundaryInSSADefBlock(SILNode *ssaDef,
611+
const PrunedLiveness &liveness) override;
612+
void
613+
findBoundaryInMultiDefBlock(SILBasicBlock *block, bool isLiveOut,
614+
const MultiDefPrunedLiveness &liveness) override;
615+
void findBoundaryInNonDefBlock(SILBasicBlock *block,
616+
const PrunedLiveness &liveness) override;
617+
};
618+
619+
/// Record the blocks which either contain last use points or are boundary edge
620+
/// targets.
621+
///
622+
/// Enables clients only interested in block-level details to avoid expensive
623+
/// and for-them wasteful instruction list iteration.
624+
struct PrunedLivenessBlockBoundary : AnyPrunedLivenessBoundary {
625+
/// Blocks containing last users or dead defs.
626+
SmallVector<SILBasicBlock *, 8> endBlocks;
627+
628+
void clear() {
629+
endBlocks.clear();
630+
boundaryEdges.clear();
631+
}
632+
633+
private:
634+
void findBoundaryInSSADefBlock(SILNode *ssaDef,
635+
const PrunedLiveness &liveness) override;
636+
void
637+
findBoundaryInMultiDefBlock(SILBasicBlock *block, bool isLiveOut,
638+
const MultiDefPrunedLiveness &liveness) override;
639+
void findBoundaryInNonDefBlock(SILBasicBlock *block,
640+
const PrunedLiveness &liveness) override;
589641
};
590642

591643
/// PrunedLiveness with information about defs for computing the live range
@@ -696,7 +748,7 @@ class PrunedLiveRange : public PrunedLiveness {
696748
/// The computed boundary will completely post-dominate, including dead end
697749
/// paths. The client should query DeadEndBlocks to ignore those dead end
698750
/// paths.
699-
void computeBoundary(PrunedLivenessBoundary &boundary) const;
751+
void computeBoundary(AnyPrunedLivenessBoundary &boundary) const;
700752

701753
/// Compute the boundary from a backward CFG traversal from a known set of
702754
/// jointly post-dominating blocks. Avoids the need to record an ordered list
@@ -772,7 +824,7 @@ class SSAPrunedLiveness : public PrunedLiveRange<SSAPrunedLiveness> {
772824

773825
/// SSA implementation of computeBoundary.
774826
void findBoundariesInBlock(SILBasicBlock *block, bool isLiveOut,
775-
PrunedLivenessBoundary &boundary) const;
827+
AnyPrunedLivenessBoundary &boundary) const;
776828

777829
/// Compute liveness for a single SSA definition. The lifetime-ending uses are
778830
/// also recorded--destroy_value or end_borrow.
@@ -860,7 +912,7 @@ class MultiDefPrunedLiveness : public PrunedLiveRange<MultiDefPrunedLiveness> {
860912

861913
/// Multi-Def implementation of computeBoundary.
862914
void findBoundariesInBlock(SILBasicBlock *block, bool isLiveOut,
863-
PrunedLivenessBoundary &boundary) const;
915+
AnyPrunedLivenessBoundary &boundary) const;
864916

865917
/// Compute liveness for all currently initialized definitions. The
866918
/// lifetime-ending uses are also recorded--destroy_value or
@@ -879,6 +931,9 @@ class MultiDefPrunedLiveness : public PrunedLiveRange<MultiDefPrunedLiveness> {
879931
/// also lack scope-ending instructions, so the liveness of their nested uses
880932
/// may be ignored.
881933
LiveRangeSummary computeSimple();
934+
935+
friend struct PrunedLivenessBoundary;
936+
friend struct PrunedLivenessBlockBoundary;
882937
};
883938

884939
//===----------------------------------------------------------------------===//

lib/SIL/Utils/PrunedLiveness.cpp

Lines changed: 80 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -822,7 +822,7 @@ bool PrunedLiveRange<LivenessWithDefs>::areUsesOutsideBoundary(
822822

823823
template <typename LivenessWithDefs>
824824
void PrunedLiveRange<LivenessWithDefs>::computeBoundary(
825-
PrunedLivenessBoundary &boundary) const {
825+
AnyPrunedLivenessBoundary &boundary) const {
826826
assert(asImpl().isInitialized());
827827

828828
for (SILBasicBlock *block : getDiscoveredBlocks()) {
@@ -896,50 +896,60 @@ template class PrunedLiveRange<MultiDefPrunedLiveness>;
896896
//===----------------------------------------------------------------------===//
897897

898898
/// Given live-within (non-live-out) \p block, find the last user.
899-
void findBoundaryInNonDefBlock(SILBasicBlock *block,
900-
PrunedLivenessBoundary &boundary,
901-
const PrunedLiveness &liveness) {
899+
void PrunedLivenessBoundary::findBoundaryInNonDefBlock(
900+
SILBasicBlock *block, const PrunedLiveness &liveness) {
902901
assert(liveness.getBlockLiveness(block) == PrunedLiveBlocks::LiveWithin);
903902

904903
for (SILInstruction &inst : llvm::reverse(*block)) {
905904
if (liveness.isInterestingUser(&inst)) {
906-
boundary.lastUsers.push_back(&inst);
905+
lastUsers.push_back(&inst);
907906
return;
908907
}
909908
}
910909
llvm_unreachable("live-within block must contain an interesting use");
911910
}
912911

912+
void PrunedLivenessBlockBoundary::findBoundaryInNonDefBlock(
913+
SILBasicBlock *block, const PrunedLiveness &liveness) {
914+
assert(liveness.getBlockLiveness(block) == PrunedLiveBlocks::LiveWithin);
915+
916+
endBlocks.push_back(block);
917+
}
918+
913919
/// Given a live-within \p block that contains an SSA definition, and knowledge
914920
/// that all live uses are dominated by that single definition, find either the
915921
/// last user or a dead def.
916922
///
917923
/// A live range with a single definition cannot have any uses above that
918924
/// definition in the same block. This even holds for unreachable self-loops.
919-
void findBoundaryInSSADefBlock(SILNode *ssaDef,
920-
PrunedLivenessBoundary &boundary,
921-
const PrunedLiveness &liveness) {
925+
void PrunedLivenessBoundary::findBoundaryInSSADefBlock(
926+
SILNode *ssaDef, const PrunedLiveness &liveness) {
922927
// defInst is null for argument defs.
923928
SILInstruction *defInst = dyn_cast<SILInstruction>(ssaDef);
924929
for (SILInstruction &inst : llvm::reverse(*ssaDef->getParentBlock())) {
925930
if (&inst == defInst) {
926-
boundary.deadDefs.push_back(cast<SILNode>(&inst));
931+
deadDefs.push_back(cast<SILNode>(&inst));
927932
return;
928933
}
929934
if (liveness.isInterestingUser(&inst)) {
930-
boundary.lastUsers.push_back(&inst);
935+
lastUsers.push_back(&inst);
931936
return;
932937
}
933938
}
934939
auto *deadArg = dyn_cast<SILArgument>(ssaDef);
935940
assert(deadArg
936941
&& "findBoundariesInBlock must be called on a live block");
937-
boundary.deadDefs.push_back(deadArg);
942+
deadDefs.push_back(deadArg);
943+
}
944+
945+
void PrunedLivenessBlockBoundary::findBoundaryInSSADefBlock(
946+
SILNode *ssaDef, const PrunedLiveness &liveness) {
947+
endBlocks.push_back(ssaDef->getParentBlock());
938948
}
939949

940950
void SSAPrunedLiveness::findBoundariesInBlock(
941951
SILBasicBlock *block, bool isLiveOut,
942-
PrunedLivenessBoundary &boundary) const {
952+
AnyPrunedLivenessBoundary &boundary) const {
943953
assert(isInitialized());
944954

945955
// For SSA, a live-out block cannot have a boundary.
@@ -948,13 +958,13 @@ void SSAPrunedLiveness::findBoundariesInBlock(
948958

949959
// Handle live-within block
950960
if (!isDefBlock(block)) {
951-
findBoundaryInNonDefBlock(block, boundary, *this);
961+
boundary.findBoundaryInNonDefBlock(block, *this);
952962
return;
953963
}
954964
// Find either the last user or a dead def
955965
auto *defInst = def->getDefiningInstruction();
956966
SILNode *defNode = defInst ? cast<SILNode>(defInst) : cast<SILArgument>(def);
957-
findBoundaryInSSADefBlock(defNode, boundary, *this);
967+
boundary.findBoundaryInSSADefBlock(defNode, *this);
958968
}
959969

960970
//===----------------------------------------------------------------------===//
@@ -1045,13 +1055,13 @@ static FunctionTest MultiDefUseLivenessTest(
10451055

10461056
void MultiDefPrunedLiveness::findBoundariesInBlock(
10471057
SILBasicBlock *block, bool isLiveOut,
1048-
PrunedLivenessBoundary &boundary) const {
1058+
AnyPrunedLivenessBoundary &boundary) const {
10491059
assert(isInitialized());
10501060

10511061
if (!isDefBlock(block)) {
10521062
// A live-out block with no defs cannot have a boundary.
10531063
if (!isLiveOut) {
1054-
findBoundaryInNonDefBlock(block, boundary, *this);
1064+
boundary.findBoundaryInNonDefBlock(block, *this);
10551065
}
10561066
return;
10571067
}
@@ -1061,48 +1071,89 @@ void MultiDefPrunedLiveness::findBoundariesInBlock(
10611071
if (++defs.begin() == defs.end()) {
10621072
// For SSA, a live-out block cannot have a boundary.
10631073
if (!isLiveOut) {
1064-
findBoundaryInSSADefBlock(*defs.begin(), boundary, *this);
1074+
boundary.findBoundaryInSSADefBlock(*defs.begin(), *this);
10651075
}
10661076
return;
10671077
}
1078+
boundary.findBoundaryInMultiDefBlock(block, isLiveOut, *this);
1079+
}
1080+
1081+
void PrunedLivenessBoundary::findBoundaryInMultiDefBlock(
1082+
SILBasicBlock *block, bool isLiveOut,
1083+
const MultiDefPrunedLiveness &liveness) {
10681084
// Handle a live-out or live-within block with potentially multiple defs
1069-
unsigned prevCount = boundary.deadDefs.size() + boundary.lastUsers.size();
1085+
unsigned prevCount = deadDefs.size() + lastUsers.size();
10701086
(void)prevCount;
10711087

10721088
bool isLive = isLiveOut;
10731089
for (auto &inst : llvm::reverse(*block)) {
10741090
// Check if the instruction is a def before checking whether it is a
10751091
// use. The same instruction can be both a dead def and boundary use.
1076-
if (isDef(&inst)) {
1092+
if (liveness.isDef(&inst)) {
10771093
if (!isLive) {
1078-
boundary.deadDefs.push_back(cast<SILNode>(&inst));
1094+
deadDefs.push_back(cast<SILNode>(&inst));
10791095
}
10801096
isLive = false;
10811097
}
10821098
// Note: the same instruction could potentially be both a dead def and last
10831099
// user. The liveness boundary supports this, although it won't happen in
10841100
// any context where we care about inserting code on the boundary.
1085-
if (!isLive && isInterestingUser(&inst)) {
1086-
boundary.lastUsers.push_back(&inst);
1101+
if (!isLive && liveness.isInterestingUser(&inst)) {
1102+
lastUsers.push_back(&inst);
10871103
isLive = true;
10881104
}
10891105
}
10901106
if (!isLive) {
10911107
for (SILArgument *deadArg : block->getArguments()) {
1092-
if (defs.contains(deadArg)) {
1093-
boundary.deadDefs.push_back(deadArg);
1108+
if (liveness.defs.contains(deadArg)) {
1109+
deadDefs.push_back(deadArg);
10941110
}
10951111
}
10961112
if (auto *predBB = block->getSinglePredecessorBlock()) {
1097-
if (getBlockLiveness(predBB) == PrunedLiveBlocks::LiveOut) {
1098-
boundary.boundaryEdges.push_back(block);
1113+
if (liveness.getBlockLiveness(predBB) == PrunedLiveBlocks::LiveOut) {
1114+
boundaryEdges.push_back(block);
10991115
}
11001116
}
11011117
}
11021118
// All live-within blocks must contain a boundary.
1103-
assert(isLiveOut
1104-
|| (prevCount < boundary.deadDefs.size() + boundary.lastUsers.size())
1105-
&& "findBoundariesInBlock must be called on a live block");
1119+
assert(isLiveOut ||
1120+
(prevCount < deadDefs.size() + lastUsers.size()) &&
1121+
"findBoundariesInBlock must be called on a live block");
1122+
}
1123+
1124+
void PrunedLivenessBlockBoundary::findBoundaryInMultiDefBlock(
1125+
SILBasicBlock *block, bool isLiveOut,
1126+
const MultiDefPrunedLiveness &liveness) {
1127+
bool isLive = isLiveOut;
1128+
for (auto &inst : llvm::reverse(*block)) {
1129+
// Check if the instruction is a def before checking whether it is a
1130+
// use. The same instruction can be both a dead def and boundary use.
1131+
if (liveness.isDef(&inst)) {
1132+
if (!isLive) {
1133+
endBlocks.push_back(block);
1134+
return;
1135+
}
1136+
isLive = false;
1137+
}
1138+
if (!isLive && liveness.isInterestingUser(&inst)) {
1139+
endBlocks.push_back(block);
1140+
return;
1141+
}
1142+
}
1143+
if (!isLive) {
1144+
for (SILArgument *deadArg : block->getArguments()) {
1145+
if (liveness.defs.contains(deadArg)) {
1146+
endBlocks.push_back(block);
1147+
return;
1148+
}
1149+
}
1150+
if (auto *predBB = block->getSinglePredecessorBlock()) {
1151+
if (liveness.getBlockLiveness(predBB) == PrunedLiveBlocks::LiveOut) {
1152+
boundaryEdges.push_back(block);
1153+
return;
1154+
}
1155+
}
1156+
}
11061157
}
11071158

11081159
LiveRangeSummary MultiDefPrunedLiveness::computeSimple() {

0 commit comments

Comments
 (0)