Skip to content

Commit d074033

Browse files
Merge pull request #27892 from ravikandhadai/valuelifetimeanalysis
[SIL Optimization] Fix a bug in isAliveAtBeginOfBlock function of ValueLifetimeAnalysis and generalize the utility
2 parents 1a1d9e6 + d195ecc commit d074033

File tree

2 files changed

+61
-16
lines changed

2 files changed

+61
-16
lines changed

include/swift/SILOptimizer/Utils/ValueLifetime.h

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,11 @@
2222

2323
namespace swift {
2424

25-
/// This computes the lifetime of a single SILValue.
26-
///
27-
/// This does not compute a set of jointly postdominating use points. Instead it
28-
/// assumes that the value's existing uses already jointly postdominate the
29-
/// definition. This makes sense for values that are returned +1 from an
30-
/// instruction, like partial_apply, and therefore must be released on all paths
31-
/// via strong_release or apply.
25+
/// Computes the lifetime frontier for a given value with respect to a
26+
/// given set of uses. The lifetime frontier is the list of instructions
27+
/// following the last uses. The set of uses can be passed by the clients
28+
/// of the analysis and can be a super set of the uses of the SILValue
29+
/// e.g. it can be the set of transitive uses of the SILValue.
3230
class ValueLifetimeAnalysis {
3331
public:
3432

@@ -70,28 +68,35 @@ class ValueLifetimeAnalysis {
7068
UsersMustPostDomDef
7169
};
7270

73-
/// Computes and returns the lifetime frontier for the value in \p frontier.
71+
/// Computes and returns the lifetime frontier for the value in \p frontier
72+
/// with respect to the set of uses in the userSet.
7473
///
7574
/// Returns true if all instructions in the frontier could be found in
7675
/// non-critical edges.
7776
/// Returns false if some frontier instructions are located on critical edges.
7877
/// In this case, if \p mode is AllowToModifyCFG, those critical edges are
79-
/// split, otherwise nothing is done and the returned \p frontier is not
80-
/// valid.
78+
/// split, otherwise the returned \p frontier consists of only those
79+
/// instructions of the frontier that are not in the critical edges. Note that
80+
/// the method getCriticalEdges can be used to retrieve the critical edges.
8181
///
8282
/// If \p deBlocks is provided, all dead-end blocks are ignored. This
8383
/// prevents unreachable-blocks to be included in the frontier.
8484
bool computeFrontier(Frontier &frontier, Mode mode,
8585
DeadEndBlocks *deBlocks = nullptr);
8686

87+
ArrayRef<std::pair<TermInst *, unsigned>> getCriticalEdges() {
88+
return criticalEdges;
89+
}
90+
8791
/// Returns true if the instruction \p Inst is located within the value's
8892
/// lifetime.
8993
/// It is assumed that \p inst is located after the value's definition.
9094
bool isWithinLifetime(SILInstruction *inst);
9195

9296
/// Returns true if the value is alive at the begin of block \p bb.
9397
bool isAliveAtBeginOfBlock(SILBasicBlock *bb) {
94-
return liveBlocks.count(bb) && bb != defValue->getParent();
98+
return liveBlocks.count(bb) &&
99+
(bb != defValue->getParent() || hasUsersBeforeDef);
95100
}
96101

97102
/// Checks if there is a dealloc_ref inside the value's live range.
@@ -112,6 +117,14 @@ class ValueLifetimeAnalysis {
112117
/// provided with the constructor.
113118
llvm::SmallPtrSet<SILInstruction *, 16> userSet;
114119

120+
/// Indicates whether the basic block containing def has users of def that
121+
/// precede def. This field is initialized by propagateLiveness.
122+
bool hasUsersBeforeDef;
123+
124+
/// Critical edges that couldn't be split to compute the frontier. This could
125+
/// be non-empty when the analysis is invoked with DontModifyCFG mode.
126+
llvm::SmallVector<std::pair<TermInst *, unsigned>, 16> criticalEdges;
127+
115128
/// Propagates the liveness information up the control flow graph.
116129
void propagateLiveness();
117130

lib/SILOptimizer/Utils/ValueLifetime.cpp

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ using namespace swift;
1818

1919
void ValueLifetimeAnalysis::propagateLiveness() {
2020
assert(liveBlocks.empty() && "frontier computed twice");
21+
assert(!userSet.count(defValue) && "definition cannot be its own use");
2122

2223
auto defBB = defValue->getParentBlock();
2324
llvm::SmallVector<SILBasicBlock *, 64> worklist;
@@ -42,13 +43,16 @@ void ValueLifetimeAnalysis::propagateLiveness() {
4243
numUsersBeforeDef--;
4344
}
4445

46+
// Initialize the hasUsersBeforeDef field.
47+
hasUsersBeforeDef = numUsersBeforeDef > 0;
48+
4549
// Now propagate liveness backwards until we hit the block that defines the
4650
// value.
4751
while (!worklist.empty()) {
4852
auto *bb = worklist.pop_back_val();
4953

5054
// Don't go beyond the definition.
51-
if (bb == defBB && numUsersBeforeDef == 0)
55+
if (bb == defBB && !hasUsersBeforeDef)
5256
continue;
5357

5458
for (SILBasicBlock *Pred : bb->getPredecessorBlocks()) {
@@ -93,13 +97,34 @@ bool ValueLifetimeAnalysis::computeFrontier(Frontier &frontier, Mode mode,
9397

9498
bool liveInSucc = false;
9599
bool deadInSucc = false;
100+
bool usedAndRedefinedInSucc = false;
96101
for (const SILSuccessor &succ : bb->getSuccessors()) {
97102
if (isAliveAtBeginOfBlock(succ)) {
98103
liveInSucc = true;
104+
if (succ == defValue->getParent()) {
105+
// Here, the basic block bb uses the value but also redefines the
106+
// value inside bb. The new value could be used by the successors
107+
// of succ and therefore could be live at the end of succ as well.
108+
usedAndRedefinedInSucc = true;
109+
}
99110
} else if (!deBlocks || !deBlocks->isDeadEnd(succ)) {
100111
deadInSucc = true;
101112
}
102113
}
114+
if (usedAndRedefinedInSucc) {
115+
// Here, the basic block bb uses the value and later redefines the value.
116+
// Therefore, this value's lifetime ends after its last use preceding the
117+
// re-definition of the value.
118+
auto ii = defValue->getReverseIterator();
119+
for (; ii != bb->rend(); ++ii) {
120+
if (userSet.count(&*ii)) {
121+
frontier.push_back(&*std::next(ii));
122+
break;
123+
}
124+
}
125+
assert(ii != bb->rend() &&
126+
"There must be a user in bb before definition");
127+
}
103128
if (!liveInSucc) {
104129
// The value is not live in any of the successor blocks. This means the
105130
// block contains a last use of the value. The next instruction after
@@ -141,15 +166,17 @@ bool ValueLifetimeAnalysis::computeFrontier(Frontier &frontier, Mode mode,
141166
}
142167
}
143168
if (needSplit) {
144-
if (mode == DontModifyCFG)
145-
return false;
146169
// We need to split the critical edge to create a frontier instruction.
147170
unhandledFrontierBlocks.insert(frontierBB);
148171
} else {
149172
// The first instruction of the exit-block is part of the frontier.
150173
frontier.push_back(&*frontierBB->begin());
151174
}
152175
}
176+
if (unhandledFrontierBlocks.size() == 0) {
177+
return true;
178+
}
179+
153180
// Split critical edges from the lifetime region to not yet handled frontier
154181
// blocks.
155182
for (SILBasicBlock *frontierPred : liveOutBlocks) {
@@ -163,12 +190,17 @@ bool ValueLifetimeAnalysis::computeFrontier(Frontier &frontier, Mode mode,
163190

164191
for (unsigned i = 0, e = succBlocks.size(); i != e; ++i) {
165192
if (unhandledFrontierBlocks.count(succBlocks[i])) {
166-
assert(mode == AllowToModifyCFG);
167193
assert(isCriticalEdge(term, i) && "actually not a critical edge?");
194+
noCriticalEdges = false;
195+
if (mode != AllowToModifyCFG) {
196+
// If the CFG need not be modified, just record the critical edge and
197+
// continue.
198+
this->criticalEdges.push_back({term, i});
199+
continue;
200+
}
168201
SILBasicBlock *newBlock = splitEdge(term, i);
169202
// The single terminator instruction is part of the frontier.
170203
frontier.push_back(&*newBlock->begin());
171-
noCriticalEdges = false;
172204
}
173205
}
174206
}

0 commit comments

Comments
 (0)