Skip to content

Commit ee8d31c

Browse files
author
Fedor Sergeev
committed
[New PM] Introducing PassInstrumentation framework
Pass Execution Instrumentation interface enables customizable instrumentation of pass execution, as per "RFC: Pass Execution Instrumentation interface" posted 06/07/2018 on llvm-dev@ The intent is to provide a common machinery to implement all the pass-execution-debugging features like print-before/after, opt-bisect, time-passes etc. Here we get a basic implementation consisting of: * PassInstrumentationCallbacks class that handles registration of callbacks and access to them. * PassInstrumentation class that handles instrumentation-point interfaces that call into PassInstrumentationCallbacks. * Callbacks accept StringRef which is just a name of the Pass right now. There were some ideas to pass an opaque wrapper for the pointer to pass instance, however it appears that pointer does not actually identify the instance (adaptors and managers might have the same address with the pass they govern). Hence it was decided to go simple for now and then later decide on what the proper mental model of identifying a "pass in a phase of pipeline" is. * Callbacks accept llvm::Any serving as a wrapper for const IRUnit*, to remove direct dependencies on different IRUnits (e.g. Analyses). * PassInstrumentationAnalysis analysis is explicitly requested from PassManager through usual AnalysisManager::getResult. All pass managers were updated to run that to get PassInstrumentation object for instrumentation calls. * Using tuples/index_sequence getAnalysisResult helper to extract generic AnalysisManager's extra args out of a generic PassManager's extra args. This is the only way I was able to explicitly run getResult for PassInstrumentationAnalysis out of a generic code like PassManager::run or RepeatedPass::run. TODO: Upon lengthy discussions we agreed to accept this as an initial implementation and then get rid of getAnalysisResult by improving RepeatedPass implementation. * PassBuilder takes PassInstrumentationCallbacks object to pass it further into PassInstrumentationAnalysis. Callbacks registration should be performed directly through PassInstrumentationCallbacks. * new-pm tests updated to account for PassInstrumentationAnalysis being run * Added PassInstrumentation tests to PassBuilderCallbacks unit tests. Other unit tests updated with registration of the now-required PassInstrumentationAnalysis. Made getName helper to return std::string (instead of StringRef initially) to fix asan builtbot failures on CGSCC tests. Reviewers: chandlerc, philip.pfaffe Differential Revision: https://reviews.llvm.org/D47858 llvm-svn: 342664
1 parent e23d0b6 commit ee8d31c

22 files changed

+720
-35
lines changed

llvm/include/llvm/Analysis/CGSCCPassManager.h

+31-2
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,10 @@ class ModuleToPostOrderCGSCCPassAdaptor
364364
InvalidSCCSet, nullptr, nullptr,
365365
InlinedInternalEdges};
366366

367+
// Request PassInstrumentation from analysis manager, will use it to run
368+
// instrumenting callbacks for the passes later.
369+
PassInstrumentation PI = AM.getResult<PassInstrumentationAnalysis>(M);
370+
367371
PreservedAnalyses PA = PreservedAnalyses::all();
368372
CG.buildRefSCCs();
369373
for (auto RCI = CG.postorder_ref_scc_begin(),
@@ -428,8 +432,17 @@ class ModuleToPostOrderCGSCCPassAdaptor
428432

429433
UR.UpdatedRC = nullptr;
430434
UR.UpdatedC = nullptr;
435+
436+
// Check the PassInstrumentation's BeforePass callbacks before
437+
// running the pass, skip its execution completely if asked to
438+
// (callback returns false).
439+
if (!PI.runBeforePass<LazyCallGraph::SCC>(Pass, *C))
440+
continue;
441+
431442
PreservedAnalyses PassPA = Pass.run(*C, CGAM, CG, UR);
432443

444+
PI.runAfterPass<LazyCallGraph::SCC>(Pass, *C);
445+
433446
// Update the SCC and RefSCC if necessary.
434447
C = UR.UpdatedC ? UR.UpdatedC : C;
435448
RC = UR.UpdatedRC ? UR.UpdatedRC : RC;
@@ -615,12 +628,20 @@ class CGSCCToFunctionPassAdaptor
615628
if (CG.lookupSCC(*N) != CurrentC)
616629
continue;
617630

618-
PreservedAnalyses PassPA = Pass.run(N->getFunction(), FAM);
631+
Function &F = N->getFunction();
632+
633+
PassInstrumentation PI = FAM.getResult<PassInstrumentationAnalysis>(F);
634+
if (!PI.runBeforePass<Function>(Pass, F))
635+
continue;
636+
637+
PreservedAnalyses PassPA = Pass.run(F, FAM);
638+
639+
PI.runAfterPass<Function>(Pass, F);
619640

620641
// We know that the function pass couldn't have invalidated any other
621642
// function's analyses (that's the contract of a function pass), so
622643
// directly handle the function analysis manager's invalidation here.
623-
FAM.invalidate(N->getFunction(), PassPA);
644+
FAM.invalidate(F, PassPA);
624645

625646
// Then intersect the preserved set so that invalidation of module
626647
// analyses will eventually occur when the module pass completes.
@@ -690,6 +711,8 @@ class DevirtSCCRepeatedPass
690711
PreservedAnalyses run(LazyCallGraph::SCC &InitialC, CGSCCAnalysisManager &AM,
691712
LazyCallGraph &CG, CGSCCUpdateResult &UR) {
692713
PreservedAnalyses PA = PreservedAnalyses::all();
714+
PassInstrumentation PI =
715+
AM.getResult<PassInstrumentationAnalysis>(InitialC, CG);
693716

694717
// The SCC may be refined while we are running passes over it, so set up
695718
// a pointer that we can update.
@@ -733,8 +756,14 @@ class DevirtSCCRepeatedPass
733756
auto CallCounts = ScanSCC(*C, CallHandles);
734757

735758
for (int Iteration = 0;; ++Iteration) {
759+
760+
if (!PI.runBeforePass<LazyCallGraph::SCC>(Pass, *C))
761+
continue;
762+
736763
PreservedAnalyses PassPA = Pass.run(*C, AM, CG, UR);
737764

765+
PI.runAfterPass<LazyCallGraph::SCC>(Pass, *C);
766+
738767
// If the SCC structure has changed, bail immediately and let the outer
739768
// CGSCC layer handle any iteration to reflect the refined structure.
740769
if (UR.UpdatedC && UR.UpdatedC != C) {
+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
//===- llvm/IR/PassInstrumentation.h ----------------------*- C++ -*-===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
/// \file
10+
///
11+
/// This file defines the Pass Instrumentation classes that provide
12+
/// instrumentation points into the pass execution by PassManager.
13+
///
14+
/// There are two main classes:
15+
/// - PassInstrumentation provides a set of instrumentation points for
16+
/// pass managers to call on.
17+
///
18+
/// - PassInstrumentationCallbacks registers callbacks and provides access
19+
/// to them for PassInstrumentation.
20+
///
21+
/// PassInstrumentation object is being used as a result of
22+
/// PassInstrumentationAnalysis (so it is intended to be easily copyable).
23+
///
24+
/// Intended scheme of use for Pass Instrumentation is as follows:
25+
/// - register instrumentation callbacks in PassInstrumentationCallbacks
26+
/// instance. PassBuilder provides helper for that.
27+
///
28+
/// - register PassInstrumentationAnalysis with all the PassManagers.
29+
/// PassBuilder handles that automatically when registering analyses.
30+
///
31+
/// - Pass Manager requests PassInstrumentationAnalysis from analysis manager
32+
/// and gets PassInstrumentation as its result.
33+
///
34+
/// - Pass Manager invokes PassInstrumentation entry points appropriately,
35+
/// passing StringRef identification ("name") of the pass currently being
36+
/// executed and IRUnit it works on. There can be different schemes of
37+
/// providing names in future, currently it is just a name() of the pass.
38+
///
39+
/// - PassInstrumentation wraps address of IRUnit into llvm::Any and passes
40+
/// control to all the registered callbacks. Note that we specifically wrap
41+
/// 'const IRUnitT*' so as to avoid any accidental changes to IR in
42+
/// instrumenting callbacks.
43+
///
44+
/// - Some instrumentation points (BeforePass) allow to control execution
45+
/// of a pass. For those callbacks returning false means pass will not be
46+
/// executed.
47+
///
48+
/// TODO: currently there is no way for a pass to opt-out of execution control
49+
/// (e.g. become unskippable). PassManager is the only entity that determines
50+
/// how pass instrumentation affects pass execution.
51+
///
52+
//===----------------------------------------------------------------------===//
53+
54+
#ifndef LLVM_IR_PASSINSTRUMENTATION_H
55+
#define LLVM_IR_PASSINSTRUMENTATION_H
56+
57+
#include "llvm/ADT/Any.h"
58+
#include "llvm/ADT/FunctionExtras.h"
59+
#include "llvm/ADT/SmallVector.h"
60+
#include "llvm/Support/TypeName.h"
61+
#include <type_traits>
62+
63+
namespace llvm {
64+
65+
class PreservedAnalyses;
66+
67+
/// This class manages callbacks registration, as well as provides a way for
68+
/// PassInstrumentation to pass control to the registered callbacks.
69+
class PassInstrumentationCallbacks {
70+
public:
71+
// Before/After Pass callbacks accept IRUnits, so they need to take them
72+
// as pointers, wrapped with llvm::Any
73+
using BeforePassFunc = bool(StringRef, Any);
74+
using AfterPassFunc = void(StringRef, Any);
75+
using BeforeAnalysisFunc = void(StringRef, Any);
76+
using AfterAnalysisFunc = void(StringRef, Any);
77+
78+
public:
79+
PassInstrumentationCallbacks() {}
80+
81+
/// Copying PassInstrumentationCallbacks is not intended.
82+
PassInstrumentationCallbacks(const PassInstrumentationCallbacks &) = delete;
83+
void operator=(const PassInstrumentationCallbacks &) = delete;
84+
85+
template <typename CallableT> void registerBeforePassCallback(CallableT C) {
86+
BeforePassCallbacks.emplace_back(std::move(C));
87+
}
88+
89+
template <typename CallableT> void registerAfterPassCallback(CallableT C) {
90+
AfterPassCallbacks.emplace_back(std::move(C));
91+
}
92+
93+
private:
94+
friend class PassInstrumentation;
95+
96+
SmallVector<llvm::unique_function<BeforePassFunc>, 4> BeforePassCallbacks;
97+
SmallVector<llvm::unique_function<AfterPassFunc>, 4> AfterPassCallbacks;
98+
};
99+
100+
/// This class provides instrumentation entry points for the Pass Manager,
101+
/// doing calls to callbacks registered in PassInstrumentationCallbacks.
102+
class PassInstrumentation {
103+
PassInstrumentationCallbacks *Callbacks;
104+
105+
public:
106+
/// Callbacks object is not owned by PassInstrumentation, its life-time
107+
/// should at least match the life-time of corresponding
108+
/// PassInstrumentationAnalysis (which usually is till the end of current
109+
/// compilation).
110+
PassInstrumentation(PassInstrumentationCallbacks *CB = nullptr)
111+
: Callbacks(CB) {}
112+
113+
/// BeforePass instrumentation point - takes \p Pass instance to be executed
114+
/// and constant reference to IR it operates on. \Returns true if pass is
115+
/// allowed to be executed.
116+
template <typename IRUnitT, typename PassT>
117+
bool runBeforePass(const PassT &Pass, const IRUnitT &IR) const {
118+
if (!Callbacks)
119+
return true;
120+
121+
bool ShouldRun = true;
122+
for (auto &C : Callbacks->BeforePassCallbacks)
123+
ShouldRun &= C(Pass.name(), llvm::Any(&IR));
124+
return ShouldRun;
125+
}
126+
127+
/// AfterPass instrumentation point - takes \p Pass instance that has
128+
/// just been executed and constant reference to IR it operates on.
129+
template <typename IRUnitT, typename PassT>
130+
void runAfterPass(const PassT &Pass, const IRUnitT &IR) const {
131+
if (Callbacks)
132+
for (auto &C : Callbacks->AfterPassCallbacks)
133+
C(Pass.name(), llvm::Any(&IR));
134+
}
135+
136+
/// Handle invalidation from the pass manager when PassInstrumentation
137+
/// is used as the result of PassInstrumentationAnalysis.
138+
///
139+
/// On attempt to invalidate just return false. There is nothing to become
140+
/// invalid here.
141+
template <typename IRUnitT, typename... ExtraArgsT>
142+
bool invalidate(IRUnitT &, const class llvm::PreservedAnalyses &,
143+
ExtraArgsT...) {
144+
return false;
145+
}
146+
};
147+
148+
} // namespace llvm
149+
150+
#endif

0 commit comments

Comments
 (0)