Skip to content

Commit de96226

Browse files
committed
SILPassManager: After a new function is pushed on the stack don't restart the pipeline on the old function.
Instead the pipeline is continued on the old function. This happens when a pass pushes a new, e.g. specialized function, on the function stack. There is no need to repeat passes which already did run on a function. It saves a little of compile time and I didn't see any significant impact on code size or performance. It also simplifies the pass manager.
1 parent d318c95 commit de96226

File tree

4 files changed

+112
-156
lines changed

4 files changed

+112
-156
lines changed

include/swift/SILOptimizer/PassManager/PassManager.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,21 @@ class SILPassManager {
4242
/// A list of registered analysis.
4343
llvm::SmallVector<SILAnalysis *, 16> Analysis;
4444

45+
/// An entry in the FunctionWorkList.
46+
struct WorklistEntry {
47+
WorklistEntry(SILFunction *F) : F(F) { }
48+
49+
SILFunction *F;
50+
51+
/// The current position in the transform-list.
52+
unsigned PipelineIdx = 0;
53+
54+
/// How many times the pipeline was restarted for the function.
55+
unsigned NumRestarts = 0;
56+
};
57+
4558
/// The worklist of functions to be processed by function passes.
46-
std::vector<SILFunction *> FunctionWorklist;
59+
std::vector<WorklistEntry> FunctionWorklist;
4760

4861
// Name of the current optimization stage for diagnostics.
4962
std::string StageName;
@@ -211,9 +224,8 @@ class SILPassManager {
211224
/// the module.
212225
void runModulePass(SILModuleTransform *SMT);
213226

214-
/// Run the passes in \p FuncTransforms on the function \p F.
215-
void runPassesOnFunction(PassList FuncTransforms, SILFunction *F,
216-
bool runToCompletion);
227+
/// Run the pass \p SFT on the function \p F.
228+
void runPassOnFunction(SILFunctionTransform *SFT, SILFunction *F);
217229

218230
/// Run the passes in \p FuncTransforms. Return true
219231
/// if the pass manager requested to stop the execution

lib/SILOptimizer/PassManager/PassManager.cpp

Lines changed: 88 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,6 @@ llvm::cl::opt<unsigned> SILNumOptPassesToRun(
4949
"sil-opt-pass-count", llvm::cl::init(UINT_MAX),
5050
llvm::cl::desc("Stop optimizing after <N> optimization passes"));
5151

52-
llvm::cl::opt<unsigned> SILFunctionPassPipelineLimit("sil-pipeline-limit",
53-
llvm::cl::init(10),
54-
llvm::cl::desc(""));
55-
5652
llvm::cl::opt<std::string> SILBreakOnFun(
5753
"sil-break-on-function", llvm::cl::init(""),
5854
llvm::cl::desc(
@@ -274,110 +270,92 @@ static bool breakBeforeRunning(StringRef fnName, StringRef passName) {
274270
return fnName == SILBreakOnFun && passName == SILBreakOnPass;
275271
}
276272

277-
void SILPassManager::runPassesOnFunction(PassList FuncTransforms,
278-
SILFunction *F,
279-
bool runToCompletion) {
280-
281-
const SILOptions &Options = getOptions();
282-
283-
CompletedPasses &completedPasses = CompletedPassesMap[F];
273+
void SILPassManager::runPassOnFunction(SILFunctionTransform *SFT,
274+
SILFunction *F) {
284275

285276
assert(analysesUnlocked() && "Expected all analyses to be unlocked!");
286277

287-
for (auto SFT : FuncTransforms) {
288-
PrettyStackTraceSILFunctionTransform X(SFT, NumPassesRun);
289-
DebugPrintEnabler DebugPrint(NumPassesRun);
290-
291-
SFT->injectPassManager(this);
292-
SFT->injectFunction(F);
293-
294-
// If nothing changed since the last run of this pass, we can skip this
295-
// pass.
296-
if (completedPasses.test((size_t)SFT->getPassKind()) &&
297-
!SILDisableSkippingPasses) {
298-
if (SILPrintPassName)
299-
llvm::dbgs() << "(Skip) Stage: " << StageName
300-
<< " Pass: " << SFT->getName()
301-
<< ", Function: " << F->getName() << "\n";
302-
continue;
303-
}
304-
305-
if (isDisabled(SFT)) {
306-
if (SILPrintPassName)
307-
llvm::dbgs() << "(Disabled) Stage: " << StageName
308-
<< " Pass: " << SFT->getName()
309-
<< ", Function: " << F->getName() << "\n";
310-
continue;
311-
}
278+
PrettyStackTraceSILFunctionTransform X(SFT, NumPassesRun);
279+
DebugPrintEnabler DebugPrint(NumPassesRun);
312280

313-
CurrentPassHasInvalidated = false;
281+
SFT->injectPassManager(this);
282+
SFT->injectFunction(F);
314283

284+
// If nothing changed since the last run of this pass, we can skip this
285+
// pass.
286+
CompletedPasses &completedPasses = CompletedPassesMap[F];
287+
if (completedPasses.test((size_t)SFT->getPassKind()) &&
288+
!SILDisableSkippingPasses) {
315289
if (SILPrintPassName)
316-
llvm::dbgs() << "#" << NumPassesRun << " Stage: " << StageName
290+
llvm::dbgs() << " (Skip) Stage: " << StageName
317291
<< " Pass: " << SFT->getName()
318292
<< ", Function: " << F->getName() << "\n";
293+
return;
294+
}
319295

320-
if (doPrintBefore(SFT, F)) {
321-
llvm::dbgs() << "*** SIL function before " << StageName << " "
322-
<< SFT->getName() << " (" << NumOptimizationIterations
323-
<< ") ***\n";
324-
F->dump(Options.EmitVerboseSIL);
325-
}
296+
if (isDisabled(SFT)) {
297+
if (SILPrintPassName)
298+
llvm::dbgs() << " (Disabled) Stage: " << StageName
299+
<< " Pass: " << SFT->getName()
300+
<< ", Function: " << F->getName() << "\n";
301+
return;
302+
}
326303

327-
llvm::sys::TimeValue StartTime = llvm::sys::TimeValue::now();
328-
Mod->registerDeleteNotificationHandler(SFT);
329-
if (breakBeforeRunning(F->getName(), SFT->getName()))
330-
LLVM_BUILTIN_DEBUGTRAP;
331-
SFT->run();
332-
assert(analysesUnlocked() && "Expected all analyses to be unlocked!");
333-
Mod->removeDeleteNotificationHandler(SFT);
334-
335-
// Did running the transform result in new functions being added
336-
// to the top of our worklist?
337-
bool newFunctionsAdded = (F != FunctionWorklist.back());
338-
339-
if (SILPrintPassTime) {
340-
auto Delta =
341-
llvm::sys::TimeValue::now().nanoseconds() - StartTime.nanoseconds();
342-
llvm::dbgs() << Delta << " (" << SFT->getName() << "," << F->getName()
343-
<< ")\n";
344-
}
304+
CurrentPassHasInvalidated = false;
345305

346-
// If this pass invalidated anything, print and verify.
347-
if (doPrintAfter(SFT, F, CurrentPassHasInvalidated && SILPrintAll)) {
348-
llvm::dbgs() << "*** SIL function after " << StageName << " "
349-
<< SFT->getName() << " (" << NumOptimizationIterations
350-
<< ") ***\n";
351-
F->dump(Options.EmitVerboseSIL);
352-
}
306+
if (SILPrintPassName)
307+
llvm::dbgs() << " #" << NumPassesRun << " Stage: " << StageName
308+
<< " Pass: " << SFT->getName()
309+
<< ", Function: " << F->getName() << "\n";
353310

354-
// Remember if this pass didn't change anything.
355-
if (!CurrentPassHasInvalidated)
356-
completedPasses.set((size_t)SFT->getPassKind());
311+
if (doPrintBefore(SFT, F)) {
312+
llvm::dbgs() << "*** SIL function before " << StageName << " "
313+
<< SFT->getName() << " (" << NumOptimizationIterations
314+
<< ") ***\n";
315+
F->dump(getOptions().EmitVerboseSIL);
316+
}
357317

358-
if (Options.VerifyAll &&
359-
(CurrentPassHasInvalidated || SILVerifyWithoutInvalidation)) {
360-
F->verify();
361-
verifyAnalyses(F);
362-
}
318+
llvm::sys::TimeValue StartTime = llvm::sys::TimeValue::now();
319+
Mod->registerDeleteNotificationHandler(SFT);
320+
if (breakBeforeRunning(F->getName(), SFT->getName()))
321+
LLVM_BUILTIN_DEBUGTRAP;
322+
SFT->run();
323+
assert(analysesUnlocked() && "Expected all analyses to be unlocked!");
324+
Mod->removeDeleteNotificationHandler(SFT);
363325

364-
++NumPassesRun;
326+
if (SILPrintPassTime) {
327+
auto Delta =
328+
llvm::sys::TimeValue::now().nanoseconds() - StartTime.nanoseconds();
329+
llvm::dbgs() << Delta << " (" << SFT->getName() << "," << F->getName()
330+
<< ")\n";
331+
}
365332

366-
if (!continueTransforming())
367-
return;
333+
// If this pass invalidated anything, print and verify.
334+
if (doPrintAfter(SFT, F, CurrentPassHasInvalidated && SILPrintAll)) {
335+
llvm::dbgs() << "*** SIL function after " << StageName << " "
336+
<< SFT->getName() << " (" << NumOptimizationIterations
337+
<< ") ***\n";
338+
F->dump(getOptions().EmitVerboseSIL);
339+
}
368340

369-
if (runToCompletion)
370-
continue;
341+
// Remember if this pass didn't change anything.
342+
if (!CurrentPassHasInvalidated)
343+
completedPasses.set((size_t)SFT->getPassKind());
371344

372-
// If running the transform resulted in new functions on the top
373-
// of the worklist, we'll return so that we can begin processing
374-
// those new functions.
375-
if (shouldRestartPipeline() || newFunctionsAdded)
376-
return;
345+
if (getOptions().VerifyAll &&
346+
(CurrentPassHasInvalidated || SILVerifyWithoutInvalidation)) {
347+
F->verify();
348+
verifyAnalyses(F);
377349
}
350+
351+
++NumPassesRun;
378352
}
379353

380354
void SILPassManager::runFunctionPasses(PassList FuncTransforms) {
355+
356+
if (FuncTransforms.empty())
357+
return;
358+
381359
BasicCalleeAnalysis *BCA = getAnalysis<BasicCalleeAnalysis>();
382360
BottomUpFunctionOrder BottomUpOrder(*Mod, BCA);
383361
auto BottomUpFunctions = BottomUpOrder.getFunctions();
@@ -395,82 +373,41 @@ void SILPassManager::runFunctionPasses(PassList FuncTransforms) {
395373
FunctionWorklist.push_back(*I);
396374
}
397375

398-
// Used to track how many times a given function has been
399-
// (partially) optimized by the function pass pipeline in this
400-
// invocation.
401-
llvm::DenseMap<SILFunction *, unsigned> CountOptimized;
402-
403-
// Count of how many iterations we've had since any function was
404-
// popped off the function worklist. This is used to ensure progress
405-
// and eliminate the chance of going into an infinite loop in cases
406-
// where (for example) we have recursive type-based specialization
376+
// The maximum number of times the pass pipeline can be restarted for a
377+
// function. This is used to ensure we are not going into an infinite loop in
378+
// cases where (for example) we have recursive type-based specialization
407379
// happening.
408-
unsigned IterationsWithoutProgress = 0;
380+
const unsigned MaxNumRestarts = 20;
409381

410-
// The maximum number of functions we'll optimize without popping
411-
// any off the worklist. This is expected to non-zero.
412-
const unsigned MaxIterationsWithoutProgress = 20;
382+
if (SILPrintPassName)
383+
llvm::dbgs() << "Start function passes at stage: " << StageName << "\n";
413384

414-
// Pop functions off the worklist, and run all function transforms
415-
// on each of them.
385+
// Run all transforms for all functions, starting at the tail of the worklist.
416386
while (!FunctionWorklist.empty() && continueTransforming()) {
417-
auto *F = FunctionWorklist.back();
387+
unsigned TailIdx = FunctionWorklist.size() - 1;
388+
unsigned PipelineIdx = FunctionWorklist[TailIdx].PipelineIdx;
389+
SILFunction *F = FunctionWorklist[TailIdx].F;
418390

419-
// If we've done many iterations without progress, pop the current
420-
// function and any other function we've run any optimizations on
421-
// on from the stack and then continue.
422-
if (IterationsWithoutProgress == (MaxIterationsWithoutProgress - 1)) {
423-
// Pop the current (potentially not-yet-optimized) function off.
391+
if (PipelineIdx >= FuncTransforms.size()) {
392+
// All passes did already run for the function. Pop it off the worklist.
424393
FunctionWorklist.pop_back();
425-
IterationsWithoutProgress = 0;
426-
427-
// Pop any remaining functions that have been optimized (at
428-
// least through some portion of the pipeline).
429-
while (!FunctionWorklist.empty() &&
430-
CountOptimized[FunctionWorklist.back()] > 0)
431-
FunctionWorklist.pop_back();
432-
433394
continue;
434395
}
435-
436-
if (CountOptimized[F] > SILFunctionPassPipelineLimit) {
437-
DEBUG(llvm::dbgs() << "*** Hit limit optimizing: " << F->getName()
438-
<< '\n');
439-
FunctionWorklist.pop_back();
440-
IterationsWithoutProgress = 0;
441-
continue;
442-
}
443-
444-
assert(
445-
!shouldRestartPipeline() &&
396+
assert(!shouldRestartPipeline() &&
446397
"Did not expect function pipeline set up to restart from beginning!");
447398

448-
assert(CountOptimized[F] <= SILFunctionPassPipelineLimit &&
449-
"Function optimization count exceeds limit!");
450-
auto runToCompletion = CountOptimized[F] == SILFunctionPassPipelineLimit;
451-
452-
runPassesOnFunction(FuncTransforms, F, runToCompletion);
453-
++CountOptimized[F];
454-
++IterationsWithoutProgress;
455-
456-
if (runToCompletion) {
457-
FunctionWorklist.pop_back();
458-
IterationsWithoutProgress = 0;
459-
clearRestartPipeline();
460-
continue;
461-
}
462-
399+
runPassOnFunction(FuncTransforms[PipelineIdx], F);
463400

464-
// If running the function transforms did not result in new
465-
// functions being added to the top of the worklist, then we're
466-
// done with this function and can pop it off and continue.
467-
// Otherwise, we'll return to this function and reoptimize after
468-
// processing the new functions that were added.
469-
if (F == FunctionWorklist.back() && !shouldRestartPipeline()) {
470-
FunctionWorklist.pop_back();
471-
IterationsWithoutProgress = 0;
401+
// Note: Don't get entry reference prior to runPassOnFunction().
402+
// A pass can push a new function to the worklist which may cause a
403+
// reallocation of the buffer and that would invalidate the reference.
404+
WorklistEntry &Entry = FunctionWorklist[TailIdx];
405+
if (shouldRestartPipeline() && Entry.NumRestarts < MaxNumRestarts) {
406+
++Entry.NumRestarts;
407+
Entry.PipelineIdx = 0;
408+
} else {
409+
++Entry.PipelineIdx;
472410
}
473-
474411
clearRestartPipeline();
475412
}
476413
}

lib/SILOptimizer/Transforms/FunctionSignatureOpts.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,13 @@ class FunctionSignatureOpts : public SILFunctionTransform {
869869
// Make sure the PM knows about this function. This will also help us
870870
// with self-recursion.
871871
notifyPassManagerOfFunction(FST.getOptimizedFunction());
872+
873+
// We have to restart the pipeline for this thunk in order to run the
874+
// inliner (and other opts) again. This is important if the new
875+
// specialized function (which is called from this thunk) is
876+
// function-signature-optimized again and also becomes an
877+
// always-inline-thunk.
878+
restartPassPipeline();
872879
}
873880
}
874881

test/SILOptimizer/inline_devirtualize_specialize.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-sil-opt -enable-sil-verify-all %s -inline -devirtualizer -generic-specializer -dce | %FileCheck %s
1+
// RUN: %target-sil-opt -enable-sil-verify-all %s -devirtualizer -generic-specializer -inline -dce | %FileCheck %s
22

33
sil_stage canonical
44

0 commit comments

Comments
 (0)