Skip to content

Commit c98d04b

Browse files
authored
Merge pull request #33351 from gottesmm/pr-2debc9735fb751cf4057c98a1d85ce39b325f38c
[semantic-arc] When computing Lifetimes include all consuming uses, not just the final destroying use.
2 parents bbac3c1 + 962106f commit c98d04b

File tree

4 files changed

+184
-77
lines changed

4 files changed

+184
-77
lines changed

include/swift/SILOptimizer/Utils/ValueLifetime.h

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,38 @@ namespace swift {
2828
/// of the analysis and can be a super set of the uses of the SILValue
2929
/// e.g. it can be the set of transitive uses of the SILValue.
3030
class ValueLifetimeAnalysis {
31+
/// The instruction or argument that define the value.
32+
PointerUnion<SILInstruction *, SILArgument *> defValue;
33+
34+
/// The set of blocks where the value is live.
35+
llvm::SmallSetVector<SILBasicBlock *, 16> liveBlocks;
36+
37+
/// The set of instructions where the value is used, or the users-list
38+
/// provided with the constructor.
39+
llvm::SmallPtrSet<SILInstruction *, 16> userSet;
40+
41+
/// Indicates whether the basic block containing def has users of def that
42+
/// precede def. This field is initialized by propagateLiveness.
43+
bool hasUsersBeforeDef;
44+
45+
/// Critical edges that couldn't be split to compute the frontier. This could
46+
/// be non-empty when the analysis is invoked with DontModifyCFG mode.
47+
llvm::SmallVector<std::pair<TermInst *, unsigned>, 16> criticalEdges;
48+
3149
public:
3250

3351
/// The lifetime frontier for the value. It is the list of instructions
3452
/// following the last uses of the value. All the frontier instructions
3553
/// end the value's lifetime.
3654
using Frontier = SmallVector<SILInstruction *, 4>;
3755

38-
/// Constructor for the value \p Def with a specific range of users.
56+
/// Constructor for the value \p def with a specific range of users.
3957
///
4058
/// We templatize over the RangeTy so that we can initialize
4159
/// ValueLifetimeAnalysis with misc iterators including transform
4260
/// iterators.
4361
template <typename RangeTy>
44-
ValueLifetimeAnalysis(SILInstruction *def, const RangeTy &userRange)
62+
ValueLifetimeAnalysis(decltype(defValue) def, const RangeTy &userRange)
4563
: defValue(def), userSet(userRange.begin(), userRange.end()) {
4664
propagateLiveness();
4765
}
@@ -102,7 +120,7 @@ class ValueLifetimeAnalysis {
102120
/// Returns true if the value is alive at the begin of block \p bb.
103121
bool isAliveAtBeginOfBlock(SILBasicBlock *bb) {
104122
return liveBlocks.count(bb) &&
105-
(bb != defValue->getParent() || hasUsersBeforeDef);
123+
(hasUsersBeforeDef || bb != getDefValueParentBlock());
106124
}
107125

108126
/// Checks if there is a dealloc_ref inside the value's live range.
@@ -112,24 +130,19 @@ class ValueLifetimeAnalysis {
112130
void dump() const;
113131

114132
private:
133+
SILFunction *getFunction() const {
134+
if (auto *inst = defValue.dyn_cast<SILInstruction *>()) {
135+
return inst->getFunction();
136+
}
137+
return defValue.get<SILArgument *>()->getFunction();
138+
}
115139

116-
/// The value.
117-
SILInstruction *defValue;
118-
119-
/// The set of blocks where the value is live.
120-
llvm::SmallSetVector<SILBasicBlock *, 16> liveBlocks;
121-
122-
/// The set of instructions where the value is used, or the users-list
123-
/// provided with the constructor.
124-
llvm::SmallPtrSet<SILInstruction *, 16> userSet;
125-
126-
/// Indicates whether the basic block containing def has users of def that
127-
/// precede def. This field is initialized by propagateLiveness.
128-
bool hasUsersBeforeDef;
129-
130-
/// Critical edges that couldn't be split to compute the frontier. This could
131-
/// be non-empty when the analysis is invoked with DontModifyCFG mode.
132-
llvm::SmallVector<std::pair<TermInst *, unsigned>, 16> criticalEdges;
140+
SILBasicBlock *getDefValueParentBlock() const {
141+
if (auto *inst = defValue.dyn_cast<SILInstruction *>()) {
142+
return inst->getParent();
143+
}
144+
return defValue.get<SILArgument *>()->getParent();
145+
}
133146

134147
/// Propagates the liveness information up the control flow graph.
135148
void propagateLiveness();

lib/SILOptimizer/Transforms/SemanticARCOpts.cpp

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,10 @@ class OwnershipLiveRange {
245245
TransformRange<ArrayRef<Operand *>, OperandToUser>;
246246
DestroyingInstsRange getDestroyingInsts() const;
247247

248+
using ConsumingInstsRange =
249+
TransformRange<ArrayRef<Operand *>, OperandToUser>;
250+
ConsumingInstsRange getAllConsumingInsts() const;
251+
248252
/// If this LiveRange has a single destroying use, return that use. Otherwise,
249253
/// return nullptr.
250254
Operand *getSingleDestroyingUse() const {
@@ -335,6 +339,11 @@ OwnershipLiveRange::getDestroyingInsts() const {
335339
return DestroyingInstsRange(getDestroyingUses(), OperandToUser());
336340
}
337341

342+
OwnershipLiveRange::ConsumingInstsRange
343+
OwnershipLiveRange::getAllConsumingInsts() const {
344+
return ConsumingInstsRange(consumingUses, OperandToUser());
345+
}
346+
338347
OwnershipLiveRange::OwnershipLiveRange(SILValue value)
339348
: introducer(*OwnedValueIntroducer::get(value)), destroyingUses(),
340349
ownershipForwardingUses(), unknownConsumingUses() {
@@ -464,36 +473,17 @@ void OwnershipLiveRange::insertEndBorrowsAtDestroys(
464473
//
465474
// TODO: Hoist this out?
466475
SILInstruction *inst = introducer.value->getDefiningInstruction();
476+
Optional<ValueLifetimeAnalysis> analysis;
467477
if (!inst) {
468-
// If our introducer was not for an inst, it should be from an arg. In such
469-
// a case, we handle one of two cases:
470-
//
471-
// 1. If we have one destroy and that destroy is the initial instruction in
472-
// the arguments block, we just insert the end_borrow here before the
473-
// destroy_value and bail. If the destroy is not the initial instruction in
474-
// the arg block, we delegate to the ValueLifetimeAnalysis code.
475-
//
476-
// 2. If we have multiple destroys, by the properties of owned values having
477-
// a linear lifetime, we know that the destroys can not both be first in the
478-
// args block since the only way that we could have two such destroys in the
479-
// arg's block is if we destructured the arg. In such a case, the
480-
// destructure instruction would have to be between the argument and any
481-
// destroy meaning the destroys could not be first. In such a case, we
482-
// delegate to the ValueLifetimeAnalysis code.
483-
auto *arg = cast<SILArgument>(introducer.value);
484-
auto *beginInst = &*arg->getParent()->begin();
485-
if (auto *singleDestroyingUse = getSingleDestroyingUse()) {
486-
if (singleDestroyingUse->getUser() == beginInst) {
487-
auto loc = RegularLocation::getAutoGeneratedLocation();
488-
SILBuilderWithScope builder(beginInst);
489-
builder.createEndBorrow(loc, newGuaranteedValue);
490-
return;
491-
}
492-
}
493-
inst = beginInst;
478+
analysis.emplace(cast<SILArgument>(introducer.value),
479+
getAllConsumingInsts());
480+
} else {
481+
analysis.emplace(inst, getAllConsumingInsts());
494482
}
495-
ValueLifetimeAnalysis analysis(inst, getDestroyingInsts());
496-
bool foundCriticalEdges = !analysis.computeFrontier(
483+
484+
// Use all consuming uses in our value lifetime analysis to ensure correctness
485+
// in the face of unreachable code.
486+
bool foundCriticalEdges = !analysis->computeFrontier(
497487
scratch, ValueLifetimeAnalysis::DontModifyCFG, &deadEndBlocks);
498488
(void)foundCriticalEdges;
499489
assert(!foundCriticalEdges);

lib/SILOptimizer/Utils/ValueLifetime.cpp

Lines changed: 61 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,23 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "swift/SILOptimizer/Utils/ValueLifetime.h"
14+
#include "swift/Basic/STLExtras.h"
1415
#include "swift/SIL/BasicBlockUtils.h"
1516
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
1617

1718
using namespace swift;
1819

1920
void ValueLifetimeAnalysis::propagateLiveness() {
21+
bool defIsInstruction = defValue.is<SILInstruction *>();
2022
assert(liveBlocks.empty() && "frontier computed twice");
21-
assert(!userSet.count(defValue) && "definition cannot be its own use");
23+
assert(
24+
(!defIsInstruction || !userSet.count(defValue.get<SILInstruction *>())) &&
25+
"definition cannot be its own use");
2226

23-
auto defBB = defValue->getParentBlock();
24-
llvm::SmallVector<SILBasicBlock *, 64> worklist;
27+
// Compute the def block only if we have a SILInstruction. If we have a
28+
// SILArgument, this will be nullptr.
29+
auto *defBB = getDefValueParentBlock();
30+
SmallVector<SILBasicBlock *, 64> worklist;
2531
int numUsersBeforeDef = 0;
2632

2733
// Find the initial set of blocks where the value is live, because
@@ -31,20 +37,28 @@ void ValueLifetimeAnalysis::propagateLiveness() {
3137
if (liveBlocks.insert(userBlock))
3238
worklist.push_back(userBlock);
3339

34-
// A user in the defBB could potentially be located before the defValue.
35-
if (userBlock == defBB)
40+
// A user in the defBB could potentially be located before the defValue. If
41+
// we had a SILArgument, defBB will be nullptr, so we should always have
42+
// numUsersBeforeDef is 0. We assert this at the end of the loop.
43+
if (defIsInstruction && userBlock == defBB)
3644
++numUsersBeforeDef;
3745
}
38-
// Don't count any users in the defBB which are actually located _after_
39-
// the defValue.
40-
auto instIter = defValue->getIterator();
41-
while (numUsersBeforeDef > 0 && ++instIter != defBB->end()) {
42-
if (userSet.count(&*instIter))
43-
--numUsersBeforeDef;
46+
assert((defValue.is<SILInstruction *>() || (numUsersBeforeDef == 0)) &&
47+
"Non SILInstruction defValue with users before the def?!");
48+
49+
// Don't count any users in the defBB which are actually located _after_ the
50+
// defValue.
51+
if (defIsInstruction) {
52+
auto instIter = defValue.get<SILInstruction *>()->getIterator();
53+
while (numUsersBeforeDef > 0 && ++instIter != defBB->end()) {
54+
if (userSet.count(&*instIter))
55+
--numUsersBeforeDef;
56+
}
4457
}
4558

4659
// Initialize the hasUsersBeforeDef field.
4760
hasUsersBeforeDef = numUsersBeforeDef > 0;
61+
assert(defIsInstruction || !hasUsersBeforeDef);
4862

4963
// Now propagate liveness backwards until we hit the block that defines the
5064
// value.
@@ -55,30 +69,31 @@ void ValueLifetimeAnalysis::propagateLiveness() {
5569
if (bb == defBB && !hasUsersBeforeDef)
5670
continue;
5771

58-
for (SILBasicBlock *Pred : bb->getPredecessorBlocks()) {
72+
for (auto *predBB : bb->getPredecessorBlocks()) {
5973
// If it's already in the set, then we've already queued and/or
6074
// processed the predecessors.
61-
if (liveBlocks.insert(Pred))
62-
worklist.push_back(Pred);
75+
if (liveBlocks.insert(predBB))
76+
worklist.push_back(predBB);
6377
}
6478
}
6579
}
6680

6781
SILInstruction *ValueLifetimeAnalysis::findLastUserInBlock(SILBasicBlock *bb) {
6882
// Walk backwards in bb looking for last use of the value.
69-
for (auto ii = bb->rbegin(); ii != bb->rend(); ++ii) {
70-
assert(defValue != &*ii && "Found def before finding use!");
83+
for (auto &inst : llvm::reverse(*bb)) {
84+
assert(defValue.dyn_cast<SILInstruction *>() != &inst &&
85+
"Found def before finding use!");
7186

72-
if (userSet.count(&*ii))
73-
return &*ii;
87+
if (userSet.count(&inst))
88+
return &inst;
7489
}
7590
llvm_unreachable("Expected to find use of value in block!");
7691
}
7792

7893
bool ValueLifetimeAnalysis::computeFrontier(Frontier &frontier, Mode mode,
7994
DeadEndBlocks *deBlocks) {
80-
assert(!isAliveAtBeginOfBlock(defValue->getFunction()->getEntryBlock())
81-
&& "Can't compute frontier for def which does not dominate all uses");
95+
assert(!isAliveAtBeginOfBlock(getFunction()->getEntryBlock()) &&
96+
"Can't compute frontier for def which does not dominate all uses");
8297

8398
bool noCriticalEdges = true;
8499

@@ -101,10 +116,16 @@ bool ValueLifetimeAnalysis::computeFrontier(Frontier &frontier, Mode mode,
101116
for (const SILSuccessor &succ : bb->getSuccessors()) {
102117
if (isAliveAtBeginOfBlock(succ)) {
103118
liveInSucc = true;
104-
if (succ == defValue->getParent()) {
119+
if (succ == getDefValueParentBlock()) {
105120
// Here, the basic block bb uses the value but also redefines the
106121
// value inside bb. The new value could be used by the successors
107122
// of succ and therefore could be live at the end of succ as well.
123+
//
124+
// This should never happen if we have a SILArgument since the
125+
// SILArgument can not have any uses before it in a block.
126+
assert(defValue.is<SILInstruction *>() &&
127+
"SILArguments dominate all instructions in their defining "
128+
"blocks");
108129
usedAndRedefinedInSucc = true;
109130
}
110131
} else if (!deBlocks || !deBlocks->isDeadEnd(succ)) {
@@ -115,7 +136,10 @@ bool ValueLifetimeAnalysis::computeFrontier(Frontier &frontier, Mode mode,
115136
// Here, the basic block bb uses the value and later redefines the value.
116137
// Therefore, this value's lifetime ends after its last use preceding the
117138
// re-definition of the value.
118-
auto ii = defValue->getReverseIterator();
139+
//
140+
// We know that we can not have a SILArgument here since the SILArgument
141+
// dominates all instructions in the same block.
142+
auto ii = defValue.get<SILInstruction *>()->getReverseIterator();
119143
for (; ii != bb->rend(); ++ii) {
120144
if (userSet.count(&*ii)) {
121145
frontier.push_back(&*std::next(ii));
@@ -245,15 +269,19 @@ bool ValueLifetimeAnalysis::isWithinLifetime(SILInstruction *inst) {
245269
// Searches \p bb backwards from the instruction before \p frontierInst
246270
// to the beginning of the list and returns true if we find a dealloc_ref
247271
// /before/ we find \p defValue (the instruction that defines our target value).
248-
static bool blockContainsDeallocRef(SILBasicBlock *bb, SILInstruction *defValue,
249-
SILInstruction *frontierInst) {
272+
static bool
273+
blockContainsDeallocRef(SILBasicBlock *bb,
274+
PointerUnion<SILInstruction *, SILArgument *> defValue,
275+
SILInstruction *frontierInst) {
250276
SILBasicBlock::reverse_iterator End = bb->rend();
251277
SILBasicBlock::reverse_iterator iter = frontierInst->getReverseIterator();
252278
for (++iter; iter != End; ++iter) {
253279
SILInstruction *inst = &*iter;
254280
if (isa<DeallocRefInst>(inst))
255281
return true;
256-
if (inst == defValue)
282+
// We know that inst is not a nullptr, so if we have a SILArgument, this
283+
// will always fail as we want.
284+
if (inst == defValue.dyn_cast<SILInstruction *>())
257285
return false;
258286
}
259287
return false;
@@ -281,9 +309,14 @@ bool ValueLifetimeAnalysis::containsDeallocRef(const Frontier &frontier) {
281309
}
282310

283311
void ValueLifetimeAnalysis::dump() const {
284-
llvm::errs() << "lifetime of def: " << *defValue;
285-
for (SILInstruction *Use : userSet) {
286-
llvm::errs() << " use: " << *Use;
312+
llvm::errs() << "lifetime of def: ";
313+
if (auto *ii = defValue.dyn_cast<SILInstruction *>()) {
314+
llvm::errs() << *ii;
315+
} else {
316+
llvm::errs() << *defValue.get<SILArgument *>();
317+
}
318+
for (SILInstruction *use : userSet) {
319+
llvm::errs() << " use: " << *use;
287320
}
288321
llvm::errs() << " live blocks:";
289322
for (SILBasicBlock *bb : liveBlocks) {

0 commit comments

Comments
 (0)