Skip to content

[SILGenCleanup] Complete lifetimes of defs backwards reachable from dead-end blocks. #78105

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Dec 19, 2024
Merged
3 changes: 3 additions & 0 deletions include/swift/SIL/BasicBlockDatastructures.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ class BasicBlockWorklist {
push(initialBlock);
}

/// Whether there are any remaining blocks to process.
bool empty() { return worklist.empty(); }

/// Pops the last added element from the worklist or returns null, if the
/// worklist is empty.
SILBasicBlock *pop() {
Expand Down
11 changes: 11 additions & 0 deletions include/swift/SIL/SILFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1565,6 +1565,17 @@ class SILFunction
}
}

/// Populate \p output with every block terminated by an unreachable
/// instruction.
void visitUnreachableTerminatedBlocks(
llvm::function_ref<void(SILBasicBlock &)> visitor) const {
for (auto &block : const_cast<SILFunction &>(*this)) {
if (isa<UnreachableInst>(block.getTerminator())) {
visitor(block);
}
}
}

//===--------------------------------------------------------------------===//
// Argument Helper Methods
//===--------------------------------------------------------------------===//
Expand Down
27 changes: 23 additions & 4 deletions include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,32 @@ class SILLoopInfo;
/// Compute the set of reachable blocks.
class ReachableBlocks {
BasicBlockSet visited;
bool isComputed;

public:
ReachableBlocks(SILFunction *function) : visited(function) {}
ReachableBlocks(SILFunction *function)
: visited(function), isComputed(false) {}

/// Populate `visited` with the blocks reachable in the function.
void compute();

/// Whether `block` is reachable from the entry block.
bool isReachable(SILBasicBlock *block) const {
assert(isComputed);
return visited.contains(block);
}

bool hasUnreachableBlocks() const {
assert(isComputed);
for (auto &block : *visited.getFunction()) {
if (!isReachable(&block)) {
return true;
}
}
return false;
}

private:
/// Invoke \p visitor for each reachable block in \p f in worklist order (at
/// least one predecessor has been visited--defs are always visited before
/// uses except for phi-type block args). The \p visitor takes a block
Expand All @@ -50,9 +72,6 @@ class ReachableBlocks {
///
/// Returns true if all reachable blocks were visited.
bool visit(function_ref<bool(SILBasicBlock *)> visitor);

/// Return true if \p bb has been visited.
bool isVisited(SILBasicBlock *bb) const { return visited.contains(bb); }
};

/// Computes the set of blocks from which a path to the return-block exists.
Expand Down
35 changes: 9 additions & 26 deletions lib/SILOptimizer/Mandatory/ClosureLifetimeFixup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -587,8 +587,7 @@ static SILValue tryRewriteToPartialApplyStack(
ConvertEscapeToNoEscapeInst *cvt, SILInstruction *closureUser,
DominanceAnalysis *dominanceAnalysis, InstructionDeleter &deleter,
llvm::DenseMap<SILInstruction *, SILInstruction *> &memoized,
llvm::DenseSet<SILBasicBlock *> &unreachableBlocks,
const bool &modifiedCFG) {
ReachableBlocks const &reachableBlocks, const bool &modifiedCFG) {

auto *origPA = dyn_cast<PartialApplyInst>(skipConvert(cvt->getOperand()));
if (!origPA)
Expand Down Expand Up @@ -972,8 +971,8 @@ static SILValue tryRewriteToPartialApplyStack(

// Don't run insertDeallocOfCapturedArguments if newPA is in an unreachable
// block insertDeallocOfCapturedArguments will run code that computes the DF
// for newPA that will loop infinetly.
if (unreachableBlocks.count(newPA->getParent()))
// for newPA that will loop infinitely.
if (!reachableBlocks.isReachable(newPA->getParent()))
return closureOp;

auto getAddressToDealloc = [&](SILValue argAddress) -> SILValue {
Expand All @@ -995,8 +994,8 @@ static bool tryExtendLifetimeToLastUse(
ConvertEscapeToNoEscapeInst *cvt, DominanceAnalysis *dominanceAnalysis,
DeadEndBlocksAnalysis *deadEndBlocksAnalysis,
llvm::DenseMap<SILInstruction *, SILInstruction *> &memoized,
llvm::DenseSet<SILBasicBlock *> &unreachableBlocks,
InstructionDeleter &deleter, const bool &modifiedCFG) {
ReachableBlocks const &reachableBlocks, InstructionDeleter &deleter,
const bool &modifiedCFG) {
// If there is a single user, this is simple: extend the
// lifetime of the operand until the use ends.
auto *singleUser = lookThroughRebastractionUsers(cvt, memoized);
Expand All @@ -1019,7 +1018,7 @@ static bool tryExtendLifetimeToLastUse(

if (SILValue closureOp = tryRewriteToPartialApplyStack(
cvt, singleUser, dominanceAnalysis, deleter, memoized,
unreachableBlocks, /*const*/ modifiedCFG)) {
reachableBlocks, /*const*/ modifiedCFG)) {
if (endAsyncLet) {
// Add the closure as a second operand to the endAsyncLet builtin.
// This ensures that the closure arguments are kept alive until the
Expand Down Expand Up @@ -1428,22 +1427,6 @@ static bool fixupCopyBlockWithoutEscaping(CopyBlockWithoutEscapingInst *cb,
return true;
}

static void computeUnreachableBlocks(
llvm::DenseSet<SILBasicBlock*> &unreachableBlocks,
SILFunction &fn) {

ReachableBlocks isReachable(&fn);
llvm::DenseSet<SILBasicBlock *> reachable;
isReachable.visit([&] (SILBasicBlock *block) -> bool {
reachable.insert(block);
return true;
});
for (auto &block : fn) {
if (!reachable.count(&block))
unreachableBlocks.insert(&block);
}
}

static bool fixupClosureLifetimes(SILFunction &fn,
DominanceAnalysis *dominanceAnalysis,
DeadEndBlocksAnalysis *deadEndBlocksAnalysis,
Expand All @@ -1454,8 +1437,8 @@ static bool fixupClosureLifetimes(SILFunction &fn,
// queries.
llvm::DenseMap<SILInstruction *, SILInstruction *> memoizedQueries;

llvm::DenseSet<SILBasicBlock *> unreachableBlocks;
computeUnreachableBlocks(unreachableBlocks, fn);
ReachableBlocks reachableBlocks(&fn);
reachableBlocks.compute();

for (auto &block : fn) {
SILSSAUpdater updater;
Expand Down Expand Up @@ -1485,7 +1468,7 @@ static bool fixupClosureLifetimes(SILFunction &fn,

if (tryExtendLifetimeToLastUse(cvt, dominanceAnalysis,
deadEndBlocksAnalysis, memoizedQueries,
unreachableBlocks, updater.getDeleter(),
reachableBlocks, updater.getDeleter(),
/*const*/ modifiedCFG)) {
changed = true;
checkStackNesting = true;
Expand Down
Loading