1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
|
#pragma once
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
//===- llvm/IR/PassInstrumentation.h ----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file defines the Pass Instrumentation classes that provide
/// instrumentation points into the pass execution by PassManager.
///
/// There are two main classes:
/// - PassInstrumentation provides a set of instrumentation points for
/// pass managers to call on.
///
/// - PassInstrumentationCallbacks registers callbacks and provides access
/// to them for PassInstrumentation.
///
/// PassInstrumentation object is being used as a result of
/// PassInstrumentationAnalysis (so it is intended to be easily copyable).
///
/// Intended scheme of use for Pass Instrumentation is as follows:
/// - register instrumentation callbacks in PassInstrumentationCallbacks
/// instance. PassBuilder provides helper for that.
///
/// - register PassInstrumentationAnalysis with all the PassManagers.
/// PassBuilder handles that automatically when registering analyses.
///
/// - Pass Manager requests PassInstrumentationAnalysis from analysis manager
/// and gets PassInstrumentation as its result.
///
/// - Pass Manager invokes PassInstrumentation entry points appropriately,
/// passing StringRef identification ("name") of the pass currently being
/// executed and IRUnit it works on. There can be different schemes of
/// providing names in future, currently it is just a name() of the pass.
///
/// - PassInstrumentation wraps address of IRUnit into llvm::Any and passes
/// control to all the registered callbacks. Note that we specifically wrap
/// 'const IRUnitT*' so as to avoid any accidental changes to IR in
/// instrumenting callbacks.
///
/// - Some instrumentation points (BeforePass) allow to control execution
/// of a pass. For those callbacks returning false means pass will not be
/// executed.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_IR_PASSINSTRUMENTATION_H
#define LLVM_IR_PASSINSTRUMENTATION_H
#include "llvm/ADT/Any.h"
#include "llvm/ADT/FunctionExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include <type_traits>
namespace llvm {
class PreservedAnalyses;
class StringRef;
/// This class manages callbacks registration, as well as provides a way for
/// PassInstrumentation to pass control to the registered callbacks.
class PassInstrumentationCallbacks {
public:
// Before/After callbacks accept IRUnits whenever appropriate, so they need
// to take them as constant pointers, wrapped with llvm::Any.
// For the case when IRUnit has been invalidated there is a different
// callback to use - AfterPassInvalidated.
// We call all BeforePassFuncs to determine if a pass should run or not.
// BeforeNonSkippedPassFuncs are called only if the pass should run.
// TODO: currently AfterPassInvalidated does not accept IRUnit, since passing
// already invalidated IRUnit is unsafe. There are ways to handle invalidated
// IRUnits in a safe way, and we might pursue that as soon as there is a
// useful instrumentation that needs it.
using BeforePassFunc = bool(StringRef, Any);
using BeforeSkippedPassFunc = void(StringRef, Any);
using BeforeNonSkippedPassFunc = void(StringRef, Any);
using AfterPassFunc = void(StringRef, Any, const PreservedAnalyses &);
using AfterPassInvalidatedFunc = void(StringRef, const PreservedAnalyses &);
using BeforeAnalysisFunc = void(StringRef, Any);
using AfterAnalysisFunc = void(StringRef, Any);
public:
PassInstrumentationCallbacks() {}
/// Copying PassInstrumentationCallbacks is not intended.
PassInstrumentationCallbacks(const PassInstrumentationCallbacks &) = delete;
void operator=(const PassInstrumentationCallbacks &) = delete;
template <typename CallableT>
void registerShouldRunOptionalPassCallback(CallableT C) {
ShouldRunOptionalPassCallbacks.emplace_back(std::move(C));
}
template <typename CallableT>
void registerBeforeSkippedPassCallback(CallableT C) {
BeforeSkippedPassCallbacks.emplace_back(std::move(C));
}
template <typename CallableT>
void registerBeforeNonSkippedPassCallback(CallableT C) {
BeforeNonSkippedPassCallbacks.emplace_back(std::move(C));
}
template <typename CallableT> void registerAfterPassCallback(CallableT C) {
AfterPassCallbacks.emplace_back(std::move(C));
}
template <typename CallableT>
void registerAfterPassInvalidatedCallback(CallableT C) {
AfterPassInvalidatedCallbacks.emplace_back(std::move(C));
}
template <typename CallableT>
void registerBeforeAnalysisCallback(CallableT C) {
BeforeAnalysisCallbacks.emplace_back(std::move(C));
}
template <typename CallableT>
void registerAfterAnalysisCallback(CallableT C) {
AfterAnalysisCallbacks.emplace_back(std::move(C));
}
/// Add a class name to pass name mapping for use by pass instrumentation.
void addClassToPassName(StringRef ClassName, StringRef PassName);
/// Get the pass name for a given pass class name.
StringRef getPassNameForClassName(StringRef ClassName);
private:
friend class PassInstrumentation;
/// These are only run on passes that are not required. They return false when
/// an optional pass should be skipped.
SmallVector<llvm::unique_function<BeforePassFunc>, 4>
ShouldRunOptionalPassCallbacks;
/// These are run on passes that are skipped.
SmallVector<llvm::unique_function<BeforeSkippedPassFunc>, 4>
BeforeSkippedPassCallbacks;
/// These are run on passes that are about to be run.
SmallVector<llvm::unique_function<BeforeNonSkippedPassFunc>, 4>
BeforeNonSkippedPassCallbacks;
/// These are run on passes that have just run.
SmallVector<llvm::unique_function<AfterPassFunc>, 4> AfterPassCallbacks;
/// These are run passes that have just run on invalidated IR.
SmallVector<llvm::unique_function<AfterPassInvalidatedFunc>, 4>
AfterPassInvalidatedCallbacks;
/// These are run on analyses that are about to be run.
SmallVector<llvm::unique_function<BeforeAnalysisFunc>, 4>
BeforeAnalysisCallbacks;
/// These are run on analyses that have been run.
SmallVector<llvm::unique_function<AfterAnalysisFunc>, 4>
AfterAnalysisCallbacks;
StringMap<std::string> ClassToPassName;
};
/// This class provides instrumentation entry points for the Pass Manager,
/// doing calls to callbacks registered in PassInstrumentationCallbacks.
class PassInstrumentation {
PassInstrumentationCallbacks *Callbacks;
// Template argument PassT of PassInstrumentation::runBeforePass could be two
// kinds: (1) a regular pass inherited from PassInfoMixin (happen when
// creating a adaptor pass for a regular pass); (2) a type-erased PassConcept
// created from (1). Here we want to make case (1) skippable unconditionally
// since they are regular passes. We call PassConcept::isRequired to decide
// for case (2).
template <typename PassT>
using has_required_t = decltype(std::declval<PassT &>().isRequired());
template <typename PassT>
static std::enable_if_t<is_detected<has_required_t, PassT>::value, bool>
isRequired(const PassT &Pass) {
return Pass.isRequired();
}
template <typename PassT>
static std::enable_if_t<!is_detected<has_required_t, PassT>::value, bool>
isRequired(const PassT &Pass) {
return false;
}
public:
/// Callbacks object is not owned by PassInstrumentation, its life-time
/// should at least match the life-time of corresponding
/// PassInstrumentationAnalysis (which usually is till the end of current
/// compilation).
PassInstrumentation(PassInstrumentationCallbacks *CB = nullptr)
: Callbacks(CB) {}
/// BeforePass instrumentation point - takes \p Pass instance to be executed
/// and constant reference to IR it operates on. \Returns true if pass is
/// allowed to be executed. These are only run on optional pass since required
/// passes must always be run. This allows these callbacks to print info when
/// they want to skip a pass.
template <typename IRUnitT, typename PassT>
bool runBeforePass(const PassT &Pass, const IRUnitT &IR) const {
if (!Callbacks)
return true;
bool ShouldRun = true;
if (!isRequired(Pass)) {
for (auto &C : Callbacks->ShouldRunOptionalPassCallbacks)
ShouldRun &= C(Pass.name(), llvm::Any(&IR));
}
if (ShouldRun) {
for (auto &C : Callbacks->BeforeNonSkippedPassCallbacks)
C(Pass.name(), llvm::Any(&IR));
} else {
for (auto &C : Callbacks->BeforeSkippedPassCallbacks)
C(Pass.name(), llvm::Any(&IR));
}
return ShouldRun;
}
/// AfterPass instrumentation point - takes \p Pass instance that has
/// just been executed and constant reference to \p IR it operates on.
/// \p IR is guaranteed to be valid at this point.
template <typename IRUnitT, typename PassT>
void runAfterPass(const PassT &Pass, const IRUnitT &IR,
const PreservedAnalyses &PA) const {
if (Callbacks)
for (auto &C : Callbacks->AfterPassCallbacks)
C(Pass.name(), llvm::Any(&IR), PA);
}
/// AfterPassInvalidated instrumentation point - takes \p Pass instance
/// that has just been executed. For use when IR has been invalidated
/// by \p Pass execution.
template <typename IRUnitT, typename PassT>
void runAfterPassInvalidated(const PassT &Pass,
const PreservedAnalyses &PA) const {
if (Callbacks)
for (auto &C : Callbacks->AfterPassInvalidatedCallbacks)
C(Pass.name(), PA);
}
/// BeforeAnalysis instrumentation point - takes \p Analysis instance
/// to be executed and constant reference to IR it operates on.
template <typename IRUnitT, typename PassT>
void runBeforeAnalysis(const PassT &Analysis, const IRUnitT &IR) const {
if (Callbacks)
for (auto &C : Callbacks->BeforeAnalysisCallbacks)
C(Analysis.name(), llvm::Any(&IR));
}
/// AfterAnalysis instrumentation point - takes \p Analysis instance
/// that has just been executed and constant reference to IR it operated on.
template <typename IRUnitT, typename PassT>
void runAfterAnalysis(const PassT &Analysis, const IRUnitT &IR) const {
if (Callbacks)
for (auto &C : Callbacks->AfterAnalysisCallbacks)
C(Analysis.name(), llvm::Any(&IR));
}
/// Handle invalidation from the pass manager when PassInstrumentation
/// is used as the result of PassInstrumentationAnalysis.
///
/// On attempt to invalidate just return false. There is nothing to become
/// invalid here.
template <typename IRUnitT, typename... ExtraArgsT>
bool invalidate(IRUnitT &, const class llvm::PreservedAnalyses &,
ExtraArgsT...) {
return false;
}
template <typename CallableT>
void pushBeforeNonSkippedPassCallback(CallableT C) {
if (Callbacks)
Callbacks->BeforeNonSkippedPassCallbacks.emplace_back(std::move(C));
}
void popBeforeNonSkippedPassCallback() {
if (Callbacks)
Callbacks->BeforeNonSkippedPassCallbacks.pop_back();
}
};
bool isSpecialPass(StringRef PassID, const std::vector<StringRef> &Specials);
} // namespace llvm
#endif
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
|