Skip to content

Commit 5394f41

Browse files
committed
Add a check to avoid infinite looping in the pass manager.
It's possible to construct programs where the optimization pass manager will just continually execute, never making progress. Add a check to the pass manager that only allows us to optimize a limited number of functions with the function passes before moving on. Unfortunately even the tiny test case that I have for this takes minutes before we bail out with the limit I've set (which is not *that* much bigger than the maximum that I saw in our build). I don't think it would be prudent to add that test to the test suite, and I haven't managed to come up with something that finishes in a more reasonable amount of time. rdar://problem/21260480
1 parent 73cccad commit 5394f41

File tree

1 file changed

+34
-1
lines changed

1 file changed

+34
-1
lines changed

lib/SILOptimizer/PassManager/PassManager.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,15 +301,44 @@ void SILPassManager::runFunctionPasses(PassList FuncTransforms) {
301301
// invocation.
302302
llvm::DenseMap<SILFunction *, unsigned> CountOptimized;
303303

304+
// Count of how many iterations we've had since any function was
305+
// popped off the function worklist. This is used to ensure progress
306+
// and eliminate the chance of going into an infinite loop in cases
307+
// where (for example) we have recursive type-based specialization
308+
// happening.
309+
unsigned IterationsWithoutProgress = 0;
310+
311+
// The maximum number of functions we'll optimize without popping
312+
// any off the worklist. This is expected to non-zero.
313+
const unsigned MaxIterationsWithoutProgress = 20;
314+
304315
// Pop functions off the worklist, and run all function transforms
305316
// on each of them.
306317
while (!FunctionWorklist.empty() && continueTransforming()) {
307318
auto *F = FunctionWorklist.back();
308319

320+
// If we've done many iterations without progress, pop the current
321+
// function and any other function we've run any optimizations on
322+
// on from the stack and then continue.
323+
if (IterationsWithoutProgress == (MaxIterationsWithoutProgress - 1)) {
324+
// Pop the current (potentially not-yet-optimized) function off.
325+
FunctionWorklist.pop_back();
326+
IterationsWithoutProgress = 0;
327+
328+
// Pop any remaining functions that have been optimized (at
329+
// least through some portion of the pipeline).
330+
while (!FunctionWorklist.empty() &&
331+
CountOptimized[FunctionWorklist.back()] > 0)
332+
FunctionWorklist.pop_back();
333+
334+
continue;
335+
}
336+
309337
if (CountOptimized[F] > SILFunctionPassPipelineLimit) {
310338
DEBUG(llvm::dbgs() << "*** Hit limit optimizing: " << F->getName()
311339
<< '\n');
312340
FunctionWorklist.pop_back();
341+
IterationsWithoutProgress = 0;
313342
continue;
314343
}
315344

@@ -323,9 +352,11 @@ void SILPassManager::runFunctionPasses(PassList FuncTransforms) {
323352

324353
runPassesOnFunction(FuncTransforms, F, runToCompletion);
325354
++CountOptimized[F];
355+
++IterationsWithoutProgress;
326356

327357
if (runToCompletion) {
328358
FunctionWorklist.pop_back();
359+
IterationsWithoutProgress = 0;
329360
clearRestartPipeline();
330361
continue;
331362
}
@@ -336,8 +367,10 @@ void SILPassManager::runFunctionPasses(PassList FuncTransforms) {
336367
// done with this function and can pop it off and continue.
337368
// Otherwise, we'll return to this function and reoptimize after
338369
// processing the new functions that were added.
339-
if (F == FunctionWorklist.back() && !shouldRestartPipeline())
370+
if (F == FunctionWorklist.back() && !shouldRestartPipeline()) {
340371
FunctionWorklist.pop_back();
372+
IterationsWithoutProgress = 0;
373+
}
341374

342375
clearRestartPipeline();
343376
}

0 commit comments

Comments
 (0)