philip.pfaffe added a comment.
Here's what I think the ownership layering should look like:
struct PassBuilder {
SmallVector<std::function<BeforePassFunc>, 4> BeforePassCallbacks;
SmallVector<std::function<AfterPassFunc>, 4> AfterPassCallbacks;
SmallVector<std::function<void()>, 2> StartPipelineCallbacks;
SmallVector<std::function<void()>, 2> EndPipelineCallbacks;
void addCallback(...);
void registerFunctionAnalyses(ModuleAnalysisManager &MAM) {
MAM.setPassInstrumentation(
new PassInstrumentation{BeforePassCallbacks, AfterPassCallbacks,
StartPipelineCallbacks, EndPipelineCalbacks});
...
}
void crossRegisterProxies(
LoopAnalysisManager &LAM, FunctionAnalysisManager &FAM,
CGSCCAnalysisManager &CGAM,
ModuleAnalysisManager &MAM) {
LAM.setPassInstrumentation(MAM.PI);
FAM.setPassInstrumentation(MAM.PI);
CGAM.setPassInstrumentation(MAM.PI);
}
};
struct PassInstrumentation {
// Callbacks
SmallVector<std::function<BeforePassFunc>, 4> BeforePassCallbacks;
SmallVector<std::function<AfterPassFunc>, 4> AfterPassCallbacks;
SmallVector<std::function<void()>, 2> StartPipelineCallbacks;
SmallVector<std::function<void()>, 2> EndPipelineCallbacks;
// Instrumentation payload
unsigned PassExecutionCounter;
};
struct AnalysisManager<T> {
std::shared_ptr<PassInstrumentation> PI;
friend struct PassInstrumentationProxy;
};
struct PassInstrumentationProxy : AnalysisInfoMixin<> {
using Result = PassInstrumentation *;
Restul run(...) { return AM->PI; }
};
PassBuilder becomes purely the callback registration entrypoint. When populating a ModuleAM, all the callbacks get passed down into it. Now propagating the instrumentation payload into the inner AMs can be either done through the AM proxies, or just statically during proxy registration, which I think is just as good but saves some indirections. Lastly passes and passmanagers access the payload through a proxy analysis.
Repository:
rL LLVM
https://reviews.llvm.org/D47858