44
44
#include " swift/SIL/ApplySite.h"
45
45
#include " swift/SIL/MemAccessUtils.h"
46
46
#include " swift/SIL/BasicBlockData.h"
47
+ #include " swift/SIL/BasicBlockDatastructures.h"
47
48
#include " swift/SILOptimizer/PassManager/Transforms.h"
48
49
#include " swift/SILOptimizer/Utils/InstOptUtils.h"
49
50
#include " swift/SILOptimizer/Utils/Devirtualize.h"
@@ -275,38 +276,33 @@ struct BlockInfo {
275
276
// / non-null if this block contains a recursive call.
276
277
SILInstruction *recursiveCall;
277
278
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;
280
283
281
284
// / True if the block has a terminator with an invariant condition.
282
285
// /
283
286
// / Note: "invariant" means: invariant with respect to the expected invariants,
284
287
// / which are passed to the constructor.
285
288
bool hasInvariantCondition;
286
289
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
288
291
// / through a recursive call?
289
292
// /
290
- // / This flag is propagated up the control flow, starting at returns.
291
- // /
292
293
// / 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;
303
296
297
+ // / Is there any path from the this block to a recursive call?
298
+ bool reachesRecursiveCall;
299
+
304
300
// / Get block information with expected \p invariants.
305
301
BlockInfo (SILBasicBlock *block, Invariants invariants) :
306
302
recursiveCall (nullptr ),
307
- numSuccsNotReachingReturn (block-> getNumSuccessors () ),
303
+ numSuccsReachingRecursiveCall ( 0 ),
308
304
hasInvariantCondition (invariants.isInvariant(block->getTerminator ())),
309
- reachesReturn (false ), reachableFromEntry (false ) {
305
+ reachesFunctionExit (false ), reachesRecursiveCall (false ) {
310
306
for (SILInstruction &inst : *block) {
311
307
if (auto applySite = FullApplySite::isa (&inst)) {
312
308
// Ignore blocks which call a @_semantics("programtermination_point").
@@ -319,22 +315,23 @@ struct BlockInfo {
319
315
if (isRecursiveCall (applySite) &&
320
316
invariants.hasInvariantArguments (applySite)) {
321
317
recursiveCall = &inst;
318
+ reachesRecursiveCall = true ;
322
319
return ;
323
320
}
324
321
}
325
322
if (invariants.isMemoryInvariant () && mayWriteToMemory (&inst)) {
326
323
// If we are assuming that all memory is invariant, a memory-writing
327
324
// 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 ;
330
327
return ;
331
328
}
332
329
}
333
330
TermInst *term = block->getTerminator ();
334
331
if (term->isFunctionExiting () ||
335
332
// Also treat non-assert-like unreachables as returns, like "exit()".
336
333
term->isProgramTerminating ()) {
337
- reachesReturn = true ;
334
+ reachesFunctionExit = true ;
338
335
}
339
336
}
340
337
};
@@ -383,91 +380,117 @@ class InfiniteRecursionAnalysis {
383
380
return BlockInfo (block, invariants);
384
381
}) { }
385
382
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 ());
391
410
392
- // Initialize the workList with all function-return blocks.
411
+ // Initialize the workList with all function-exiting blocks.
393
412
for (auto bd : blockInfos) {
394
- if (bd.data .reachesReturn )
413
+ if (bd.data .reachesFunctionExit )
395
414
workList.push_back (&bd.block );
396
415
}
397
416
398
417
while (!workList.empty ()) {
399
418
SILBasicBlock *block = workList.pop_back_val ();
419
+ BlockInfo &info = blockInfos[block];
420
+ assert (info.reachesFunctionExit );
400
421
for (auto *pred : block->getPredecessorBlocks ()) {
401
422
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 ||
403
432
// Recursive calls block the flag propagation.
404
433
predInfo.recursiveCall != nullptr )
405
434
continue ;
406
435
407
- assert (predInfo.numSuccsNotReachingReturn > 0 );
408
- predInfo.numSuccsNotReachingReturn -= 1 ;
409
-
410
436
// 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.
413
439
// 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).
417
444
if (predInfo.hasInvariantCondition &&
418
- predInfo.numSuccsNotReachingReturn > 0 )
445
+ predInfo.numSuccsReachingRecursiveCall > 0 )
419
446
continue ;
420
447
421
- predInfo.reachesReturn = true ;
448
+ predInfo.reachesFunctionExit = true ;
422
449
workList.push_back (pred);
423
450
}
424
451
}
425
- return blockInfos.entry ().data .reachesReturn ;
426
452
}
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 ;
427
461
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 );
436
464
437
- bool foundInfiniteRecursion = false ;
438
- while (!workList.empty ()) {
439
- SILBasicBlock *block = workList.pop_back_val ();
465
+ while (SILBasicBlock *block = workList.pop ()) {
440
466
if (auto *recursiveCall = blockInfos[block].recursiveCall ) {
441
467
blockInfos.getFunction ()->getModule ().getASTContext ().Diags .diagnose (
442
468
recursiveCall->getLoc ().getSourceLoc (),
443
469
diag::warn_infinite_recursive_call);
444
- foundInfiniteRecursion = true ;
445
470
continue ;
446
471
}
447
472
for (auto *succ : block->getSuccessorBlocks ()) {
448
473
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);
453
476
}
454
477
}
455
- return foundInfiniteRecursion ;
478
+ return true ;
456
479
}
457
480
458
481
public:
459
482
460
483
LLVM_ATTRIBUTE_USED void dump () {
461
484
for (auto bd : blockInfos) {
462
485
llvm::dbgs () << " bb" << bd.block .getDebugID ()
463
- << " : numSuccs= " << bd.data .numSuccsNotReachingReturn ;
486
+ << " : numSuccs= " << bd.data .numSuccsReachingRecursiveCall ;
464
487
if (bd.data .recursiveCall )
465
488
llvm::dbgs () << " hasRecursiveCall" ;
466
489
if (bd.data .hasInvariantCondition )
467
490
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 )
471
494
llvm::dbgs () << " reachesRecursiveCall" ;
472
495
llvm::dbgs () << ' \n ' ;
473
496
}
@@ -477,20 +500,9 @@ class InfiniteRecursionAnalysis {
477
500
// / Returns true, if at least one recursive call is found.
478
501
static bool analyzeAndDiagnose (SILFunction *function, Invariants invariants) {
479
502
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 ();
494
506
}
495
507
};
496
508
0 commit comments