Skip to content

[SILOptimizer] Clean up infinite recursion diagnostic pass #19710

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
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 27 additions & 25 deletions lib/SILOptimizer/Mandatory/DiagnoseInfiniteRecursion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,37 +94,39 @@ static bool hasRecursiveCallInPath(SILBasicBlock &Block,
return false;
}

static bool hasInfinitelyRecursiveApply(SILFunction &Fn,
SILFunction *TargetFn) {
SmallPtrSet<SILBasicBlock *, 16> Visited;
SmallVector<SILBasicBlock *, 16> WorkList;
// Keep track of whether we found at least one recursive path.
/// Perform a DFS through the target function to find any paths to an exit node
/// that do not call into the target.
static bool hasInfinitelyRecursiveApply(SILFunction *targetFn) {
SmallPtrSet<SILBasicBlock *, 32> visited = { targetFn->getEntryBlock() };
SmallVector<SILBasicBlock *, 32> workList = { targetFn->getEntryBlock() };

// Keep track of if we've found any recursive blocks at all.
// We return true if we found any recursion and did not find any
// non-recursive, function-exiting blocks.
bool foundRecursion = false;
auto *targetModule = targetFn->getModule().getSwiftModule();

auto *TargetModule = TargetFn->getModule().getSwiftModule();
auto analyzeSuccessor = [&](SILBasicBlock *Succ) -> bool {
if (!Visited.insert(Succ).second)
return false;
while (!workList.empty()) {
SILBasicBlock *curBlock = workList.pop_back_val();

// If the successor block contains a recursive call, end analysis there.
if (!hasRecursiveCallInPath(*Succ, TargetFn, TargetModule)) {
WorkList.push_back(Succ);
return false;
// We're looking for functions that are recursive on _all_ paths. If this
// block is recursive, mark that we found recursion and check the next
// block in the work list.
if (hasRecursiveCallInPath(*curBlock, targetFn, targetModule)) {
foundRecursion = true;
continue;
}
return true;
};

// Seed the work list with the entry block.
foundRecursion |= analyzeSuccessor(Fn.getEntryBlock());

while (!WorkList.empty()) {
SILBasicBlock *CurBlock = WorkList.pop_back_val();
// Found a path to the exit node without a recursive call.
if (CurBlock->getTerminator()->isFunctionExiting())
// If this block doesn't have a recursive call, and it exits the function,
// then we know the function is not infinitely recursive.
if (curBlock->getTerminator()->isFunctionExiting())
return false;

for (SILBasicBlock *Succ : CurBlock->getSuccessorBlocks())
foundRecursion |= analyzeSuccessor(Succ);
// Otherwise, push the successors onto the stack if we haven't visited them.
for (auto *succ : curBlock->getSuccessorBlocks()) {
if (visited.insert(succ).second)
workList.push_back(succ);
}
}
return foundRecursion;
}
Expand All @@ -149,7 +151,7 @@ namespace {
if (!Fn->hasLocation() || Fn->getLocation().getSourceLoc().isInvalid())
return;

if (hasInfinitelyRecursiveApply(*Fn, Fn)) {
if (hasInfinitelyRecursiveApply(Fn)) {
diagnose(Fn->getModule().getASTContext(),
Fn->getLocation().getSourceLoc(),
diag::warn_infinite_recursive_function);
Expand Down