Skip to content

Commit 397a25c

Browse files
Merge pull request #68608 from nate-chandler/rdar115468707
[CanonicalizeOSSALifetime] Extend lexical lifetimes to unreachables.
2 parents f3166b3 + b902ad7 commit 397a25c

File tree

8 files changed

+244
-22
lines changed

8 files changed

+244
-22
lines changed

include/swift/SIL/OSSALifetimeCompletion.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ class OSSALifetimeCompletion {
8585
: LifetimeCompletion::AlreadyComplete;
8686
}
8787

88+
static void visitUnreachableLifetimeEnds(
89+
SILValue value, const SSAPrunedLiveness &liveness,
90+
llvm::function_ref<void(UnreachableInst *)> visit);
91+
8892
protected:
8993
bool analyzeAndUpdateLifetime(SILValue value, bool forceBoundaryCompletion);
9094
};

include/swift/SIL/PrunedLiveness.h

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,6 @@ class DeadEndBlocks;
154154
/// blocks to the blocks that occur on or after the def block. If any uses is
155155
/// not dominated by a def block, then liveness will include the entry block,
156156
/// as if defined by a function argument
157-
///
158-
/// TODO: For efficiency, use BasicBlockBitfield rather than SmallDenseMap.
159157
class PrunedLiveBlocks {
160158
public:
161159
/// Per-block liveness state computed during backward dataflow propagation.
@@ -200,6 +198,17 @@ class PrunedLiveBlocks {
200198
assert(!discoveredBlocks || discoveredBlocks->empty());
201199
}
202200

201+
PrunedLiveBlocks(PrunedLiveBlocks const &other,
202+
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
203+
: liveBlocks(other.liveBlocks.getFunction(), 2),
204+
discoveredBlocks(discoveredBlocks) {
205+
assert(!discoveredBlocks || other.discoveredBlocks);
206+
for (auto &block : *other.liveBlocks.getFunction()) {
207+
liveBlocks.set(&block, other.liveBlocks.get(&block));
208+
}
209+
initializedFlag = other.initializedFlag;
210+
}
211+
203212
bool isInitialized() const { return initializedFlag; }
204213

205214
void initializeDiscoveredBlocks(
@@ -363,6 +372,10 @@ class PrunedLiveness {
363372
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
364373
: liveBlocks(function, discoveredBlocks) {}
365374

375+
PrunedLiveness(PrunedLiveness const &other,
376+
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
377+
: liveBlocks(other.liveBlocks, discoveredBlocks), users(other.users) {}
378+
366379
bool isInitialized() const { return liveBlocks.isInitialized(); }
367380

368381
bool empty() const { return users.empty(); }
@@ -531,6 +544,10 @@ class PrunedLiveRange : public PrunedLiveness {
531544
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
532545
: PrunedLiveness(function, discoveredBlocks) {}
533546

547+
PrunedLiveRange(PrunedLiveRange const &other,
548+
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
549+
: PrunedLiveness(other, discoveredBlocks) {}
550+
534551
LiveRangeSummary recursivelyUpdateForDef(SILValue initialDef,
535552
ValueSet &visited,
536553
SILValue value);
@@ -628,6 +645,14 @@ class SSAPrunedLiveness : public PrunedLiveRange<SSAPrunedLiveness> {
628645
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
629646
: PrunedLiveRange(function, discoveredBlocks) {}
630647

648+
SSAPrunedLiveness(
649+
SSAPrunedLiveness const &other,
650+
SmallVectorImpl<SILBasicBlock *> *discoveredBlocks = nullptr)
651+
: PrunedLiveRange(other, discoveredBlocks) {
652+
def = other.def;
653+
defInst = other.defInst;
654+
}
655+
631656
SILValue getDef() const { return def; }
632657

633658
void initializeDef(SILValue def) {

include/swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ class CanonicalizeOSSALifetime final {
319319

320320
currentDef = def;
321321

322-
if (maximizeLifetime) {
322+
if (maximizeLifetime || respectsDeinitBarriers()) {
323323
liveness->initializeDiscoveredBlocks(&discoveredBlocks);
324324
}
325325
liveness->initializeDef(getCurrentDef());
@@ -396,10 +396,18 @@ class CanonicalizeOSSALifetime final {
396396
UserRange getUsers() const { return liveness->getAllUsers(); }
397397

398398
private:
399+
bool respectsDeinitBarriers() const {
400+
if (!currentDef->isLexical())
401+
return false;
402+
auto &module = currentDef->getFunction()->getModule();
403+
return module.getASTContext().SILOpts.supportsLexicalLifetimes(module);
404+
}
405+
399406
void recordDebugValue(DebugValueInst *dvi) { debugValues.insert(dvi); }
400407

401-
void recordConsumingUse(Operand *use) {
402-
consumingBlocks.insert(use->getUser()->getParent());
408+
void recordConsumingUse(Operand *use) { recordConsumingUser(use->getUser()); }
409+
void recordConsumingUser(SILInstruction *user) {
410+
consumingBlocks.insert(user->getParent());
403411
}
404412
bool computeCanonicalLiveness();
405413

lib/SIL/Utils/OSSALifetimeCompletion.cpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,9 @@ static bool endLifetimeAtBoundary(SILValue value,
9999
return changed;
100100
}
101101

102-
static bool endLifetimeAtUnreachableBlocks(SILValue value,
103-
const SSAPrunedLiveness &liveness) {
102+
void OSSALifetimeCompletion::visitUnreachableLifetimeEnds(
103+
SILValue value, const SSAPrunedLiveness &liveness,
104+
llvm::function_ref<void(UnreachableInst *)> visit) {
104105
PrunedLivenessBoundary boundary;
105106
liveness.computeBoundary(boundary);
106107

@@ -119,20 +120,28 @@ static bool endLifetimeAtUnreachableBlocks(SILValue value,
119120
}
120121
// Forward CFG walk from the non-lifetime-ending boundary to the unreachable
121122
// instructions.
122-
bool changed = false;
123123
while (auto *block = deadEndBlocks.pop()) {
124124
if (block->succ_empty()) {
125125
// This assert will fail unless there are already lifetime-ending
126126
// instruction on all paths to normal function exits.
127127
auto *unreachable = cast<UnreachableInst>(block->getTerminator());
128-
SILBuilderWithScope builder(unreachable);
129-
endOSSALifetime(value, builder);
130-
changed = true;
128+
visit(unreachable);
131129
}
132130
for (auto *successor : block->getSuccessorBlocks()) {
133131
deadEndBlocks.pushIfNotVisited(successor);
134132
}
135133
}
134+
}
135+
136+
static bool endLifetimeAtUnreachableBlocks(SILValue value,
137+
const SSAPrunedLiveness &liveness) {
138+
bool changed = false;
139+
OSSALifetimeCompletion::visitUnreachableLifetimeEnds(
140+
value, liveness, [&](auto *unreachable) {
141+
SILBuilderWithScope builder(unreachable);
142+
endOSSALifetime(value, builder);
143+
changed = true;
144+
});
136145
return changed;
137146
}
138147

lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -810,17 +810,47 @@ SILInstruction *SILCombiner::visitCondFailInst(CondFailInst *CFI) {
810810
if (!I->getValue().getBoolValue())
811811
return eraseInstFromFunction(*CFI);
812812

813-
// Remove any code that follows a (cond_fail 1) and set the block's
814-
// terminator to unreachable.
813+
// Remove non-lifetime-ending code that follows a (cond_fail 1) and set the
814+
// block's terminator to unreachable.
815815

816-
// Nothing more to do here
816+
// Are there instructions after this point to delete?
817+
818+
// First check if the next instruction is unreachable.
817819
if (isa<UnreachableInst>(std::next(SILBasicBlock::iterator(CFI))))
818820
return nullptr;
819821

820-
// Collect together all the instructions after this point
822+
// Otherwise, check if the only instructions are unreachables and destroys of
823+
// lexical values.
824+
825+
// Collect all instructions and, in OSSA, the values they define.
821826
llvm::SmallVector<SILInstruction *, 32> ToRemove;
822-
for (auto Inst = CFI->getParent()->rbegin(); &*Inst != CFI; ++Inst)
823-
ToRemove.push_back(&*Inst);
827+
ValueSet DefinedValues(CFI->getFunction());
828+
for (auto Iter = std::next(CFI->getIterator());
829+
Iter != CFI->getParent()->end(); ++Iter) {
830+
if (!CFI->getFunction()->hasOwnership()) {
831+
ToRemove.push_back(&*Iter);
832+
continue;
833+
}
834+
835+
for (auto result : Iter->getResults()) {
836+
DefinedValues.insert(result);
837+
}
838+
// Look for destroys of lexical values whose def isn't after the cond_fail.
839+
if (auto *dvi = dyn_cast<DestroyValueInst>(&*Iter)) {
840+
auto value = dvi->getOperand();
841+
if (!DefinedValues.contains(value) && value->isLexical())
842+
continue;
843+
}
844+
ToRemove.push_back(&*Iter);
845+
}
846+
847+
unsigned instructionsToDelete = ToRemove.size();
848+
// If the last instruction is an unreachable already, it needn't be deleted.
849+
if (isa<UnreachableInst>(ToRemove.back())) {
850+
--instructionsToDelete;
851+
}
852+
if (instructionsToDelete == 0)
853+
return nullptr;
824854

825855
for (auto *Inst : ToRemove) {
826856
// Replace any still-remaining uses with undef and erase.

lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
#include "swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h"
6969
#include "swift/SIL/InstructionUtils.h"
7070
#include "swift/SIL/NodeDatastructures.h"
71+
#include "swift/SIL/OSSALifetimeCompletion.h"
7172
#include "swift/SIL/OwnershipUtils.h"
7273
#include "swift/SIL/PrunedLiveness.h"
7374
#include "swift/SIL/Test.h"
@@ -249,6 +250,35 @@ void CanonicalizeOSSALifetime::extendLivenessToDeinitBarriers() {
249250
SmallVector<SILInstruction *, 4> outsideDestroys;
250251
findDestroysOutsideBoundary(outsideDestroys);
251252

253+
// OSSALifetimeCompletion: With complete lifetimes, creating completeLiveness
254+
// and using it to visiti unreachable lifetime ends should be deleted.
255+
SmallVector<SILBasicBlock *, 32> discoveredBlocks(this->discoveredBlocks);
256+
SSAPrunedLiveness completeLiveness(*liveness, &discoveredBlocks);
257+
258+
for (auto *end : outsideDestroys) {
259+
completeLiveness.updateForUse(end, /*lifetimeEnding*/ true);
260+
}
261+
262+
OSSALifetimeCompletion::visitUnreachableLifetimeEnds(
263+
getCurrentDef(), completeLiveness, [&](auto *unreachable) {
264+
recordConsumingUser(unreachable);
265+
if (auto *previous = unreachable->getPreviousInstruction()) {
266+
if (liveness->isInterestingUser(previous) ==
267+
PrunedLiveness::IsInterestingUser::NonUser) {
268+
liveness->updateForUse(previous, /*lifetimeEnding=*/false);
269+
}
270+
return;
271+
}
272+
for (auto *predecessor :
273+
unreachable->getParent()->getPredecessorBlocks()) {
274+
auto *previous = &predecessor->back();
275+
if (liveness->isInterestingUser(previous) ==
276+
PrunedLiveness::IsInterestingUser::NonUser) {
277+
liveness->updateForUse(previous, /*lifetimeEnding=*/false);
278+
}
279+
}
280+
});
281+
252282
auto *def = getCurrentDef()->getDefiningInstruction();
253283
using InitialBlocks = ArrayRef<SILBasicBlock *>;
254284
auto *defBlock = getCurrentDef()->getParentBlock();
@@ -890,6 +920,27 @@ static void insertDestroyBeforeInstruction(SILInstruction *nextInstruction,
890920
SILValue currentDef,
891921
CanonicalOSSAConsumeInfo &consumes,
892922
InstModCallbacks &callbacks) {
923+
// OSSALifetimeCompletion: This conditional clause can be deleted with
924+
// complete lifetimes.
925+
if (isa<UnreachableInst>(nextInstruction)) {
926+
// Don't create a destroy_value if the next instruction is an unreachable.
927+
// If there was a destroy here already, it would be reused. Avoids
928+
// creating an explicit destroy of a value which might have an unclosed
929+
// borrow scope. Doing so would result in
930+
//
931+
// somewhere:
932+
// %def
933+
// %borrow = begin_borrow ...
934+
//
935+
// die:
936+
// destroy_value %def
937+
// unreachable
938+
//
939+
// which is invalid (although the verifier doesn't catch
940+
// it--rdar://115850528) because there must be an `end_borrow %borrow`
941+
// before the destroy_value.
942+
return;
943+
}
893944
SILBuilderWithScope builder(nextInstruction);
894945
auto loc =
895946
RegularLocation::getAutoGeneratedLocation(nextInstruction->getLoc());
@@ -1124,7 +1175,7 @@ bool CanonicalizeOSSALifetime::computeLiveness() {
11241175
clear();
11251176
return false;
11261177
}
1127-
if (currentDef->isLexical()) {
1178+
if (respectsDeinitBarriers()) {
11281179
extendLivenessToDeinitBarriers();
11291180
}
11301181
if (accessBlockAnalysis) {

0 commit comments

Comments
 (0)