Skip to content
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