Files
swift-mirror/include/swift/SILOptimizer/PassManager/PassManager.h
Roman Levenstein f0a39e9e14 Add support for collecting various SIL optimizer counters
This patch implements collection and dumping of statistics about SILModules, SILFunctions and memory consumption during the execution of SIL optimization pipelines.

The following statistics can be collected:
  *  For SILFunctions: the number of SIL basic blocks, the number of SIL instructions, the number of SIL instructions of a specific kind, duration of a pass
  *  For SILModules: the number of SIL basic blocks, the number of SIL instructions, the number of SIL instructions of a specific kind, the number of SILFunctions, the amount of memory used by the compiler, duration of a pass

By default, any collection of statistics is disabled to avoid affecting compile times.

One can enable the collection of statistics and dumping of these statistics for the whole SILModule and/or for SILFunctions.

To reduce the amount of produced data, one can set thresholds in such a way that changes in the statistics are only reported if the delta between the old and the new values are at least X%. The deltas are computed as using the following formula:

   Delta = (NewValue - OldValue) / OldValue

Thresholds provide a simple way to perform a simple filtering of the collected statistics during the compilation. But if there is a need for a more complex analysis of collected data (e.g. aggregation by a pipeline stage or by the type of a transformation), it is often better to dump as much data as possible into a file using e.g. -sil-stats-dump-all -sil-stats-modules -sil-stats-functions and then e.g. use the helper scripts to store the collected data into a database and then perform complex queries on it. Many kinds of analysis can be then formulated pretty easily as SQL queries.
2017-09-10 21:47:55 -07:00

305 lines
9.9 KiB
C++

//===--- PassManager.h - Swift Pass Manager --------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/SILOptimizer/Analysis/Analysis.h"
#include "swift/SILOptimizer/PassManager/PassPipeline.h"
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include <vector>
#ifndef SWIFT_SILOPTIMIZER_PASSMANAGER_PASSMANAGER_H
#define SWIFT_SILOPTIMIZER_PASSMANAGER_PASSMANAGER_H
namespace swift {
class SILFunction;
class SILFunctionTransform;
class SILModule;
class SILModuleTransform;
class SILOptions;
class SILTransform;
namespace irgen {
class IRGenModule;
}
/// \brief The SIL pass manager.
class SILPassManager {
/// The module that the pass manager will transform.
SILModule *Mod;
/// An optional IRGenModule associated with this PassManager.
irgen::IRGenModule *IRMod;
/// The list of transformations to run.
llvm::SmallVector<SILTransform *, 16> Transformations;
/// A list of registered analysis.
llvm::SmallVector<SILAnalysis *, 16> Analysis;
/// An entry in the FunctionWorkList.
struct WorklistEntry {
WorklistEntry(SILFunction *F) : F(F) { }
SILFunction *F;
/// The current position in the transform-list.
unsigned PipelineIdx = 0;
/// How many times the pipeline was restarted for the function.
unsigned NumRestarts = 0;
};
/// The worklist of functions to be processed by function passes.
std::vector<WorklistEntry> FunctionWorklist;
// Name of the current optimization stage for diagnostics.
std::string StageName;
/// The number of passes run so far.
unsigned NumPassesRun = 0;
/// Number of optimization iterations run.
unsigned NumOptimizationIterations = 0;
/// A mask which has one bit for each pass. A one for a pass-bit means that
/// the pass doesn't need to run, because nothing has changed since the
/// previous run of that pass.
typedef std::bitset<(size_t)PassKind::AllPasses_Last + 1> CompletedPasses;
/// A completed-passes mask for each function.
llvm::DenseMap<SILFunction *, CompletedPasses> CompletedPassesMap;
/// Stores for each function the number of levels of specializations it is
/// derived from an original function. E.g. if a function is a signature
/// optimized specialization of a generic specialization, it has level 2.
/// This is used to avoid an infinite amount of functions pushed on the
/// worklist (e.g. caused by a bug in a specializing optimization).
llvm::DenseMap<SILFunction *, int> DerivationLevels;
/// Set to true when a pass invalidates an analysis.
bool CurrentPassHasInvalidated = false;
/// True if we need to stop running passes and restart again on the
/// same function.
bool RestartPipeline = false;
/// The IRGen SIL passes. These have to be dynamically added by IRGen.
llvm::DenseMap<unsigned, SILTransform *> IRGenPasses;
public:
/// C'tor. It creates and registers all analysis passes, which are defined
/// in Analysis.def.
SILPassManager(SILModule *M, llvm::StringRef Stage = "");
/// C'tor. It creates an IRGen pass manager. Passes can query for the
/// IRGenModule.
SILPassManager(SILModule *M, irgen::IRGenModule *IRMod,
llvm::StringRef Stage = "");
const SILOptions &getOptions() const;
/// \brief Searches for an analysis of type T in the list of registered
/// analysis. If the analysis is not found, the program terminates.
template<typename T>
T *getAnalysis() {
for (SILAnalysis *A : Analysis)
if (auto *R = llvm::dyn_cast<T>(A))
return R;
llvm_unreachable("Unable to find analysis for requested type.");
}
/// \returns the module that the pass manager owns.
SILModule *getModule() { return Mod; }
/// \returns the associated IGenModule or null if this is not an IRGen
/// pass manager.
irgen::IRGenModule *getIRGenModule() { return IRMod; }
/// \brief Run one iteration of the optimization pipeline.
void runOneIteration();
/// \brief Restart the function pass pipeline on the same function
/// that is currently being processed.
void restartWithCurrentFunction(SILTransform *T);
void clearRestartPipeline() { RestartPipeline = false; }
bool shouldRestartPipeline() { return RestartPipeline; }
/// \brief Iterate over all analysis and invalidate them.
void invalidateAllAnalysis() {
// Invalidate the analysis (unless they are locked)
for (auto AP : Analysis)
if (!AP->isLocked())
AP->invalidate();
CurrentPassHasInvalidated = true;
// Assume that all functions have changed. Clear all masks of all functions.
CompletedPassesMap.clear();
}
/// \brief Add the function \p F to the function pass worklist.
/// If not null, the function \p DerivedFrom is the function from which \p F
/// is derived. This is used to avoid an infinite amount of functions pushed
/// on the worklist (e.g. caused by a bug in a specializing optimization).
void addFunctionToWorklist(SILFunction *F, SILFunction *DerivedFrom);
/// \brief Iterate over all analysis and notify them of the function.
/// This function does not necessarily have to be newly created function. It
/// is the job of the analysis to make sure no extra work is done if the
/// particular analysis has been done on the function.
void notifyAnalysisOfFunction(SILFunction *F) {
for (auto AP : Analysis)
AP->notifyAddFunction(F);
}
/// \brief Broadcast the invalidation of the function to all analysis.
void invalidateAnalysis(SILFunction *F,
SILAnalysis::InvalidationKind K) {
// Invalidate the analysis (unless they are locked)
for (auto AP : Analysis)
if (!AP->isLocked())
AP->invalidate(F, K);
CurrentPassHasInvalidated = true;
// Any change let all passes run again.
CompletedPassesMap[F].reset();
}
/// \brief Iterate over all analysis and notify them of a change in witness-
/// or vtables.
void invalidateFunctionTables() {
// Invalidate the analysis (unless they are locked)
for (auto AP : Analysis)
if (!AP->isLocked())
AP->invalidateFunctionTables();
CurrentPassHasInvalidated = true;
// Assume that all functions have changed. Clear all masks of all functions.
CompletedPassesMap.clear();
}
/// \brief Iterate over all analysis and notify them of a deleted function.
void notifyDeleteFunction(SILFunction *F) {
// Invalidate the analysis (unless they are locked)
for (auto AP : Analysis)
if (!AP->isLocked())
AP->notifyDeleteFunction(F);
CurrentPassHasInvalidated = true;
// Any change let all passes run again.
CompletedPassesMap[F].reset();
}
/// \brief Reset the state of the pass manager and remove all transformation
/// owned by the pass manager. Analysis passes will be kept.
void resetAndRemoveTransformations();
/// \brief Set the name of the current optimization stage.
///
/// This is useful for debugging.
void setStageName(llvm::StringRef NextStage = "");
/// \brief Get the name of the current optimization stage.
///
/// This is useful for debugging.
StringRef getStageName() const;
/// D'tor.
~SILPassManager();
/// Verify all analyses.
void verifyAnalyses() const {
for (auto *A : Analysis) {
A->verify();
}
}
/// Verify all analyses, limiting the verification to just this one function
/// if possible.
///
/// Discussion: We leave it up to the analyses to decide how to implement
/// this. If no override is provided the SILAnalysis should just call the
/// normal verify method.
void verifyAnalyses(SILFunction *F) const {
for (auto *A : Analysis) {
A->verify(F);
}
}
void executePassPipelinePlan(const SILPassPipelinePlan &Plan) {
for (const SILPassPipeline &Pipeline : Plan.getPipelines()) {
setStageName(Pipeline.Name);
resetAndRemoveTransformations();
for (PassKind Kind : Plan.getPipelinePasses(Pipeline)) {
addPass(Kind);
}
execute();
}
}
void registerIRGenPass(PassKind Kind, SILTransform *Transform) {
assert(IRGenPasses.find(unsigned(Kind)) == IRGenPasses.end() &&
"Pass already registered");
assert(
IRMod &&
"Attempting to register an IRGen pass with a non-IRGen pass manager");
IRGenPasses[unsigned(Kind)] = Transform;
}
private:
void execute() {
runOneIteration();
}
/// Add a pass of a specific kind.
void addPass(PassKind Kind);
/// Add a pass with a given name.
void addPassForName(StringRef Name);
/// Run the SIL module transform \p SMT over all the functions in
/// the module.
void runModulePass(SILModuleTransform *SMT);
/// Run the pass \p SFT on the function \p F.
void runPassOnFunction(SILFunctionTransform *SFT, SILFunction *F);
/// Run the passes in \p FuncTransforms. Return true
/// if the pass manager requested to stop the execution
/// of the optimization cycle (this is a debug feature).
void runFunctionPasses(ArrayRef<SILFunctionTransform *> FuncTransforms);
/// A helper function that returns (based on SIL stage and debug
/// options) whether we should continue running passes.
bool continueTransforming();
/// Return true if all analyses are unlocked.
bool analysesUnlocked();
/// Displays the call graph in an external dot-viewer.
/// This function is meant for use from the debugger.
/// When asserts are disabled, this is a NoOp.
void viewCallGraph();
};
} // end namespace swift
#endif