4444#include " swift/SIL/ApplySite.h"
4545#include " swift/SIL/MemAccessUtils.h"
4646#include " swift/SIL/BasicBlockData.h"
47+ #include " swift/SIL/BasicBlockDatastructures.h"
4748#include " swift/SILOptimizer/PassManager/Transforms.h"
4849#include " swift/SILOptimizer/Utils/InstOptUtils.h"
4950#include " swift/SILOptimizer/Utils/Devirtualize.h"
@@ -275,38 +276,33 @@ struct BlockInfo {
275276 // / non-null if this block contains a recursive call.
276277 SILInstruction *recursiveCall;
277278
278- // / The number of successors which reach a `return`.
279- unsigned numSuccsNotReachingReturn;
279+ // / The number of successors which reach a recursive call, but not the
280+ // / function exit, i.e. successors for which
281+ // / reachesRecursiveCall && !reachesFunctionExit
282+ unsigned numSuccsReachingRecursiveCall;
280283
281284 // / True if the block has a terminator with an invariant condition.
282285 // /
283286 // / Note: "invariant" means: invariant with respect to the expected invariants,
284287 // / which are passed to the constructor.
285288 bool hasInvariantCondition;
286289
287- // / Is there any path from the this block to a function return , without going
290+ // / Is there any path from the this block to a function exit , without going
288291 // / through a recursive call?
289292 // /
290- // / This flag is propagated up the control flow, starting at returns.
291- // /
292293 // / Note that if memory is expected to be invariant, all memory-writing
293- // / instructions are also considered as a "return".
294- bool reachesReturn;
295-
296- // / Is there any path from the entry to this block without going through a
297- // / `reachesReturn` block.
298- // /
299- // / This flag is propagated down the control flow, starting at entry. If this
300- // / flag reaches a block with a recursiveCall, it means that it's an infinite
301- // / recursive call.
302- bool reachableFromEntry;
294+ // / instructions are also considered as a "function exit".
295+ bool reachesFunctionExit;
303296
297+ // / Is there any path from the this block to a recursive call?
298+ bool reachesRecursiveCall;
299+
304300 // / Get block information with expected \p invariants.
305301 BlockInfo (SILBasicBlock *block, Invariants invariants) :
306302 recursiveCall (nullptr ),
307- numSuccsNotReachingReturn (block-> getNumSuccessors () ),
303+ numSuccsReachingRecursiveCall ( 0 ),
308304 hasInvariantCondition (invariants.isInvariant(block->getTerminator ())),
309- reachesReturn (false ), reachableFromEntry (false ) {
305+ reachesFunctionExit (false ), reachesRecursiveCall (false ) {
310306 for (SILInstruction &inst : *block) {
311307 if (auto applySite = FullApplySite::isa (&inst)) {
312308 // Ignore blocks which call a @_semantics("programtermination_point").
@@ -319,22 +315,23 @@ struct BlockInfo {
319315 if (isRecursiveCall (applySite) &&
320316 invariants.hasInvariantArguments (applySite)) {
321317 recursiveCall = &inst;
318+ reachesRecursiveCall = true ;
322319 return ;
323320 }
324321 }
325322 if (invariants.isMemoryInvariant () && mayWriteToMemory (&inst)) {
326323 // If we are assuming that all memory is invariant, a memory-writing
327324 // instruction potentially breaks the infinite recursion loop. For the
328- // sake of the anlaysis, it's like a function return .
329- reachesReturn = true ;
325+ // sake of the anlaysis, it's like a function exit .
326+ reachesFunctionExit = true ;
330327 return ;
331328 }
332329 }
333330 TermInst *term = block->getTerminator ();
334331 if (term->isFunctionExiting () ||
335332 // Also treat non-assert-like unreachables as returns, like "exit()".
336333 term->isProgramTerminating ()) {
337- reachesReturn = true ;
334+ reachesFunctionExit = true ;
338335 }
339336 }
340337};
@@ -383,91 +380,117 @@ class InfiniteRecursionAnalysis {
383380 return BlockInfo (block, invariants);
384381 }) { }
385382
386- // / Propagates the `reachesReturn` flags up the control flow and returns true
387- // / if the flag reaches the entry block.
388- bool isEntryReachableFromReturn () {
389- // Contains blocks for which the `reachesReturn` flag is set.
390- SmallVector<SILBasicBlock *, 32 > workList;
383+ // / Propagates the `reachesRecursiveCall` flags up the control flow.
384+ void propagateRecursiveCalls () {
385+ StackList<SILBasicBlock *> workList (blockInfos.getFunction ());
386+
387+ // Initialize the workList with all blocks which contain recursive calls.
388+ for (auto bd : blockInfos) {
389+ if (bd.data .reachesRecursiveCall )
390+ workList.push_back (&bd.block );
391+ }
392+
393+ while (!workList.empty ()) {
394+ SILBasicBlock *block = workList.pop_back_val ();
395+ assert (blockInfos[block].reachesRecursiveCall );
396+ for (auto *pred : block->getPredecessorBlocks ()) {
397+ BlockInfo &predInfo = blockInfos[pred];
398+ predInfo.numSuccsReachingRecursiveCall += 1 ;
399+ if (!predInfo.reachesRecursiveCall ) {
400+ predInfo.reachesRecursiveCall = true ;
401+ workList.push_back (pred);
402+ }
403+ }
404+ }
405+ }
406+
407+ // / Propagates the `reachesFunctionExit` flags up the control flow.
408+ void propagateFunctionExits () {
409+ StackList<SILBasicBlock *> workList (blockInfos.getFunction ());
391410
392- // Initialize the workList with all function-return blocks.
411+ // Initialize the workList with all function-exiting blocks.
393412 for (auto bd : blockInfos) {
394- if (bd.data .reachesReturn )
413+ if (bd.data .reachesFunctionExit )
395414 workList.push_back (&bd.block );
396415 }
397416
398417 while (!workList.empty ()) {
399418 SILBasicBlock *block = workList.pop_back_val ();
419+ BlockInfo &info = blockInfos[block];
420+ assert (info.reachesFunctionExit );
400421 for (auto *pred : block->getPredecessorBlocks ()) {
401422 BlockInfo &predInfo = blockInfos[pred];
402- if (predInfo.reachesReturn ||
423+
424+ if (info.reachesRecursiveCall ) {
425+ // Update `numSuccsReachingRecursiveCall`, because this counter
426+ // excludes successors which reach a function exit.
427+ assert (predInfo.numSuccsReachingRecursiveCall > 0 );
428+ predInfo.numSuccsReachingRecursiveCall -= 1 ;
429+ }
430+
431+ if (predInfo.reachesFunctionExit ||
403432 // Recursive calls block the flag propagation.
404433 predInfo.recursiveCall != nullptr )
405434 continue ;
406435
407- assert (predInfo.numSuccsNotReachingReturn > 0 );
408- predInfo.numSuccsNotReachingReturn -= 1 ;
409-
410436 // This is the trick for handling invariant conditions: usually the
411- // `reachesReturn ` flag is propagated if _any_ of the successors has it
412- // set.
437+ // `reachesFunctionExit ` flag is propagated if _any_ of the successors
438+ // has it set.
413439 // For invariant conditions, it's only propagated if _all_ successors
414- // have it set. If at least one of the successors reaches a recursive
415- // call and this successor is taken once, it will be taken forever
416- // (because the condition is invariant).
440+ // which reach recursive calls also reach a function exit.
441+ // If at least one of the successors reaches a recursive call (but not
442+ // a function exit) and this successor is taken once, it will be taken
443+ // forever (because the condition is invariant).
417444 if (predInfo.hasInvariantCondition &&
418- predInfo.numSuccsNotReachingReturn > 0 )
445+ predInfo.numSuccsReachingRecursiveCall > 0 )
419446 continue ;
420447
421- predInfo.reachesReturn = true ;
448+ predInfo.reachesFunctionExit = true ;
422449 workList.push_back (pred);
423450 }
424451 }
425- return blockInfos.entry ().data .reachesReturn ;
426452 }
453+
454+ // / Finds all infinite recursive calls reachable from the entry and issues
455+ // / warnings.
456+ // / Returns true if the function contains infinite recursive calls.
457+ bool issueWarningsForInfiniteRecursiveCalls () {
458+ const BlockInfo &entryInfo = blockInfos.entry ().data ;
459+ if (!entryInfo.reachesRecursiveCall || entryInfo.reachesFunctionExit )
460+ return false ;
427461
428- // / Propagates the `reachableFromEntry` flags down the control flow and
429- // / issues a warning if it reaches a recursive call.
430- // / Returns true, if at least one recursive call is found.
431- bool findRecursiveCallsAndDiagnose () {
432- SmallVector<SILBasicBlock *, 32 > workList;
433- auto entry = blockInfos.entry ();
434- entry.data .reachableFromEntry = true ;
435- workList.push_back (&entry.block );
462+ BasicBlockWorklist workList (blockInfos.getFunction ());
463+ workList.push (&blockInfos.entry ().block );
436464
437- bool foundInfiniteRecursion = false ;
438- while (!workList.empty ()) {
439- SILBasicBlock *block = workList.pop_back_val ();
465+ while (SILBasicBlock *block = workList.pop ()) {
440466 if (auto *recursiveCall = blockInfos[block].recursiveCall ) {
441467 blockInfos.getFunction ()->getModule ().getASTContext ().Diags .diagnose (
442468 recursiveCall->getLoc ().getSourceLoc (),
443469 diag::warn_infinite_recursive_call);
444- foundInfiniteRecursion = true ;
445470 continue ;
446471 }
447472 for (auto *succ : block->getSuccessorBlocks ()) {
448473 BlockInfo &succInfo = blockInfos[succ];
449- if (!succInfo.reachesReturn && !succInfo.reachableFromEntry ) {
450- succInfo.reachableFromEntry = true ;
451- workList.push_back (succ);
452- }
474+ if (succInfo.reachesRecursiveCall && !succInfo.reachesFunctionExit )
475+ workList.pushIfNotVisited (succ);
453476 }
454477 }
455- return foundInfiniteRecursion ;
478+ return true ;
456479 }
457480
458481public:
459482
460483 LLVM_ATTRIBUTE_USED void dump () {
461484 for (auto bd : blockInfos) {
462485 llvm::dbgs () << " bb" << bd.block .getDebugID ()
463- << " : numSuccs= " << bd.data .numSuccsNotReachingReturn ;
486+ << " : numSuccs= " << bd.data .numSuccsReachingRecursiveCall ;
464487 if (bd.data .recursiveCall )
465488 llvm::dbgs () << " hasRecursiveCall" ;
466489 if (bd.data .hasInvariantCondition )
467490 llvm::dbgs () << " hasInvariantCondition" ;
468- if (bd.data .reachesReturn )
469- llvm::dbgs () << " reachesReturn " ;
470- if (bd.data .reachableFromEntry )
491+ if (bd.data .reachesFunctionExit )
492+ llvm::dbgs () << " reachesFunctionExit " ;
493+ if (bd.data .reachesRecursiveCall )
471494 llvm::dbgs () << " reachesRecursiveCall" ;
472495 llvm::dbgs () << ' \n ' ;
473496 }
@@ -477,20 +500,9 @@ class InfiniteRecursionAnalysis {
477500 // / Returns true, if at least one recursive call is found.
478501 static bool analyzeAndDiagnose (SILFunction *function, Invariants invariants) {
479502 InfiniteRecursionAnalysis analysis (function, invariants);
480- if (analysis.isEntryReachableFromReturn ())
481- return false ;
482-
483- // Now we know that the function never returns.
484- // There can be three cases:
485- // 1. All paths end up in an abnormal program termination, like fatalError().
486- // We don't want to warn about this. It's probably intention.
487- // 2. There is an infinite loop.
488- // We don't want to warn about this either. Maybe it's intention. Anyway,
489- // this case is handled by the DiagnoseUnreachable pass.
490- // 3. There is an infinite recursion.
491- // That's what we are interested in. We do a forward propagation to find
492- // the actual infinite recursive call(s) - if any.
493- return analysis.findRecursiveCallsAndDiagnose ();
503+ analysis.propagateRecursiveCalls ();
504+ analysis.propagateFunctionExits ();
505+ return analysis.issueWarningsForInfiniteRecursiveCalls ();
494506 }
495507};
496508
0 commit comments