Skip to content

Commit 2325293

Browse files
Merge pull request #41358 from nate-chandler/lexical_lifetimes/shrink-borrow-scope/adopt-reachability
[ShrinkBorrowScope] Adopt BackwardReachability.
2 parents 3708676 + 41fdbc1 commit 2325293

File tree

8 files changed

+583
-258
lines changed

8 files changed

+583
-258
lines changed

include/swift/SIL/SILBasicBlock.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,12 @@ public SwiftObjectHeader {
426426
/// the debug scope for newly created instructions.
427427
const SILDebugScope *getScopeOfFirstNonMetaInstruction();
428428

429+
/// Whether the block has any phi arguments.
430+
///
431+
/// Note that a block could have an argument and still return false. The
432+
/// argument must also satisfy SILPhiArgument::isPhiArgument.
433+
bool hasPhi() const;
434+
429435
//===--------------------------------------------------------------------===//
430436
// Debugging
431437
//===--------------------------------------------------------------------===//

include/swift/SILOptimizer/Analysis/Reachability.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
#include "swift/SIL/BasicBlockDatastructures.h"
2828
#include "swift/SIL/SILBasicBlock.h"
29+
#include "swift/SIL/SILSuccessor.h"
30+
#include "llvm/ADT/STLExtras.h"
2931

3032
namespace swift {
3133

@@ -44,16 +46,19 @@ namespace swift {
4446
/// // Mark the beginning of a block reachable. Only called once per block.
4547
/// // Typically a BasicBlockSet wrapper.
4648
/// void markReachableBegin(SILBasicBlock *block);
47-
///
49+
///
4850
/// // Mark the end of a block reachable. Only called once per block.
4951
/// // Typically a BasicBlockSet wrapper.
5052
/// void markReachableEnd(SILBasicBlock *block);
5153
///
5254
/// // Return true if \p inst is a barrier. Called once for each reachable
53-
/// // instruction, assuming that each lastUse is itself a barrier.
55+
/// // instruction, assuming that each lastUsePoint is itself a barrier.
5456
/// // Used by the data flow client to perform additional book-keeping,
5557
/// // such as recording debug_value instructions.
5658
/// bool checkReachableBarrier(SILInstruction *inst);
59+
///
60+
/// // Return true if any of \p block's phis are barriers.
61+
/// bool checkReachablePhiBarrier(SILBasicBlock *block);
5762
/// };
5863
template <typename BlockReachability>
5964
class BackwardReachability {
@@ -70,7 +75,8 @@ class BackwardReachability {
7075
// Initialize data flow starting points before running solveBackward.
7176
void initLastUse(SILInstruction *lastUsePoint) {
7277
auto *lastUseBlock = lastUsePoint->getParent();
73-
if (canReachBlockBegin(lastUsePoint)) {
78+
auto *previous = lastUsePoint->getPreviousInstruction();
79+
if (!previous || canReachBlockBegin(previous)) {
7480
pushPreds(lastUseBlock);
7581
}
7682
}
@@ -116,6 +122,10 @@ class BackwardReachability {
116122

117123
// Propagate global data flow from \p succBB to its predecessors.
118124
void pushPreds(SILBasicBlock *succBB) {
125+
if (succBB->hasPhi())
126+
if (reachableBlocks.checkReachablePhiBarrier(succBB))
127+
return;
128+
119129
reachableBlocks.markReachableBegin(succBB);
120130

121131
for (SILBasicBlock *predBB : succBB->getPredecessorBlocks()) {

include/swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ class CanonicalizeBorrowScope {
148148
};
149149

150150
bool shrinkBorrowScope(
151-
BeginBorrowInst *bbi, InstructionDeleter &deleter,
151+
BeginBorrowInst const &bbi, InstructionDeleter &deleter,
152152
SmallVectorImpl<CopyValueInst *> &modifiedCopyValueInsts);
153153

154154
bool foldDestroysOfCopiedLexicalBorrow(BeginBorrowInst *bbi,

lib/SIL/IR/SILBasicBlock.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,15 @@ bool SILBasicBlock::isLegalToHoistInto() const {
395395
return true;
396396
}
397397

398+
bool SILBasicBlock::hasPhi() const {
399+
if (getArguments().size() == 0)
400+
return false;
401+
// It is sufficient to check whether the first argument is a phi. A block
402+
// can't have both phis and terminator results.
403+
auto *argument = getArguments()[0];
404+
return argument->isPhiArgument();
405+
}
406+
398407
const SILDebugScope *SILBasicBlock::getScopeOfFirstNonMetaInstruction() {
399408
for (auto &Inst : *this)
400409
if (Inst.isMetaInstruction())

lib/SILOptimizer/Transforms/CopyPropagation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ void CopyPropagation::run() {
451451
// at least once and then until each stops making changes.
452452
while (true) {
453453
SmallVector<CopyValueInst *, 4> modifiedCopyValueInsts;
454-
auto shrunk = shrinkBorrowScope(bbi, deleter, modifiedCopyValueInsts);
454+
auto shrunk = shrinkBorrowScope(*bbi, deleter, modifiedCopyValueInsts);
455455
for (auto *cvi : modifiedCopyValueInsts)
456456
defWorklist.updateForCopy(cvi);
457457
changed |= shrunk;

lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ class DeinitBarriers {
215215
DeinitBarriers &result;
216216
SILInstruction *storageDefInst = nullptr; // null for function args
217217

218+
enum class Classification { DeadUser, Barrier, Other };
219+
218220
BackwardReachability<DestroyReachability> reachability;
219221

220222
public:
@@ -245,37 +247,87 @@ class DeinitBarriers {
245247
result.destroyReachesEndBlocks.insert(block);
246248
}
247249

248-
bool checkReachableBarrier(SILInstruction *inst);
250+
Classification classifyInstruction(SILInstruction *inst);
251+
252+
bool classificationIsBarrier(Classification classification);
253+
254+
void visitedInstruction(SILInstruction *instruction,
255+
Classification classification);
256+
257+
bool checkReachableBarrier(SILInstruction *);
258+
259+
bool checkReachablePhiBarrier(SILBasicBlock *);
249260

250261
void solveBackward() { reachability.solveBackward(); }
251262
};
252263
};
253264

254-
/// Return true if \p inst is a barrier.
255-
///
256-
/// Called exactly once for each reachable instruction. This is guaranteed to
257-
/// hold as a barrier occurs between any original destroys that are reachable
258-
/// from each. Any path reaching multiple destroys requires initialization,
259-
/// which is a storageUser and therefore a barrier.
260-
bool DeinitBarriers::DestroyReachability::checkReachableBarrier(
261-
SILInstruction *inst) {
265+
DeinitBarriers::DestroyReachability::Classification
266+
DeinitBarriers::DestroyReachability::classifyInstruction(SILInstruction *inst) {
262267
if (knownUses.debugInsts.contains(inst)) {
263-
result.deadUsers.push_back(inst);
264-
return false;
268+
return Classification::DeadUser;
265269
}
266270
if (inst == storageDefInst) {
267271
result.barriers.push_back(inst);
268-
return true;
272+
return Classification::Barrier;
269273
}
270274
if (knownUses.storageUsers.contains(inst)) {
271-
result.barriers.push_back(inst);
272-
return true;
275+
return Classification::Barrier;
273276
}
274277
if (isDeinitBarrier(inst)) {
275-
result.barriers.push_back(inst);
278+
return Classification::Barrier;
279+
}
280+
return Classification::Other;
281+
}
282+
283+
bool DeinitBarriers::DestroyReachability::classificationIsBarrier(
284+
Classification classification) {
285+
switch (classification) {
286+
case Classification::DeadUser:
287+
case Classification::Other:
288+
return false;
289+
case Classification::Barrier:
276290
return true;
277291
}
278-
return false;
292+
llvm_unreachable("exhaustive switch is not exhaustive?!");
293+
}
294+
295+
void DeinitBarriers::DestroyReachability::visitedInstruction(
296+
SILInstruction *instruction, Classification classification) {
297+
assert(classifyInstruction(instruction) == classification);
298+
switch (classification) {
299+
case Classification::DeadUser:
300+
result.deadUsers.push_back(instruction);
301+
break;
302+
case Classification::Barrier:
303+
result.barriers.push_back(instruction);
304+
break;
305+
case Classification::Other:
306+
break;
307+
}
308+
}
309+
310+
/// Return true if \p inst is a barrier.
311+
///
312+
/// Called exactly once for each reachable instruction. This is guaranteed to
313+
/// hold as a barrier occurs between any original destroys that are reachable
314+
/// from each. Any path reaching multiple destroys requires initialization,
315+
/// which is a storageUser and therefore a barrier.
316+
bool DeinitBarriers::DestroyReachability::checkReachableBarrier(
317+
SILInstruction *instruction) {
318+
auto classification = classifyInstruction(instruction);
319+
visitedInstruction(instruction, classification);
320+
return classificationIsBarrier(classification);
321+
}
322+
323+
bool DeinitBarriers::DestroyReachability::checkReachablePhiBarrier(
324+
SILBasicBlock *block) {
325+
assert(llvm::all_of(block->getArguments(),
326+
[&](auto argument) { return PhiValue(argument); }));
327+
return llvm::any_of(block->getPredecessorBlocks(), [&](auto *predecessor) {
328+
return classificationIsBarrier(
329+
classifyInstruction(predecessor->getTerminator()));
330+
});
279331
}
280332

281333
/// Algorithm for hoisting the destroys of a single uniquely identified storage

0 commit comments

Comments
 (0)