AnalysisManager.h
11.9 KB
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
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
//===- AnalysisManager.h - Analysis Management Infrastructure ---*- C++ -*-===//
//
// Part of the MLIR 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
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_PASS_ANALYSISMANAGER_H
#define MLIR_PASS_ANALYSISMANAGER_H
#include "mlir/IR/Module.h"
#include "mlir/Pass/PassInstrumentation.h"
#include "mlir/Support/LLVM.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Support/TypeName.h"
namespace mlir {
/// A special type used by analyses to provide an address that identifies a
/// particular analysis set or a concrete analysis type.
using AnalysisID = ClassID;
//===----------------------------------------------------------------------===//
// Analysis Preservation and Concept Modeling
//===----------------------------------------------------------------------===//
namespace detail {
/// A utility class to represent the analyses that are known to be preserved.
class PreservedAnalyses {
public:
/// Mark all analyses as preserved.
void preserveAll() { preservedIDs.insert(&allAnalysesID); }
/// Returns true if all analyses were marked preserved.
bool isAll() const { return preservedIDs.count(&allAnalysesID); }
/// Returns true if no analyses were marked preserved.
bool isNone() const { return preservedIDs.empty(); }
/// Preserve the given analyses.
template <typename AnalysisT> void preserve() {
preserve(AnalysisID::getID<AnalysisT>());
}
template <typename AnalysisT, typename AnalysisT2, typename... OtherAnalysesT>
void preserve() {
preserve<AnalysisT>();
preserve<AnalysisT2, OtherAnalysesT...>();
}
void preserve(const AnalysisID *id) { preservedIDs.insert(id); }
/// Returns if the given analysis has been marked as preserved. Note that this
/// simply checks for the presence of a given analysis ID and should not be
/// used as a general preservation checker.
template <typename AnalysisT> bool isPreserved() const {
return isPreserved(AnalysisID::getID<AnalysisT>());
}
bool isPreserved(const AnalysisID *id) const {
return preservedIDs.count(id);
}
private:
/// An identifier used to represent all potential analyses.
constexpr static AnalysisID allAnalysesID = {};
/// The set of analyses that are known to be preserved.
SmallPtrSet<const void *, 2> preservedIDs;
};
namespace analysis_impl {
/// Trait to check if T provides a static 'isInvalidated' method.
template <typename T, typename... Args>
using has_is_invalidated = decltype(std::declval<T &>().isInvalidated(
std::declval<const PreservedAnalyses &>()));
/// Implementation of 'isInvalidated' if the analysis provides a definition.
template <typename AnalysisT>
std::enable_if_t<is_detected<has_is_invalidated, AnalysisT>::value, bool>
isInvalidated(AnalysisT &analysis, const PreservedAnalyses &pa) {
return analysis.isInvalidated(pa);
}
/// Default implementation of 'isInvalidated'.
template <typename AnalysisT>
std::enable_if_t<!is_detected<has_is_invalidated, AnalysisT>::value, bool>
isInvalidated(AnalysisT &analysis, const PreservedAnalyses &pa) {
return !pa.isPreserved<AnalysisT>();
}
} // end namespace analysis_impl
/// The abstract polymorphic base class representing an analysis.
struct AnalysisConcept {
virtual ~AnalysisConcept() = default;
/// A hook used to query analyses for invalidation. Given a preserved analysis
/// set, returns true if it should truly be invalidated. This allows for more
/// fine-tuned invalidation in cases where an analysis wasn't explicitly
/// marked preserved, but may be preserved(or invalidated) based upon other
/// properties such as analyses sets.
virtual bool isInvalidated(const PreservedAnalyses &pa) = 0;
};
/// A derived analysis model used to hold a specific analysis object.
template <typename AnalysisT> struct AnalysisModel : public AnalysisConcept {
template <typename... Args>
explicit AnalysisModel(Args &&... args)
: analysis(std::forward<Args>(args)...) {}
/// A hook used to query analyses for invalidation.
bool isInvalidated(const PreservedAnalyses &pa) final {
return analysis_impl::isInvalidated(analysis, pa);
}
/// The actual analysis object.
AnalysisT analysis;
};
/// This class represents a cache of analyses for a single operation. All
/// computation, caching, and invalidation of analyses takes place here.
class AnalysisMap {
/// A mapping between an analysis id and an existing analysis instance.
using ConceptMap =
DenseMap<const AnalysisID *, std::unique_ptr<AnalysisConcept>>;
/// Utility to return the name of the given analysis class.
template <typename AnalysisT> static StringRef getAnalysisName() {
StringRef name = llvm::getTypeName<AnalysisT>();
if (!name.consume_front("mlir::"))
name.consume_front("(anonymous namespace)::");
return name;
}
public:
explicit AnalysisMap(Operation *ir) : ir(ir) {}
/// Get an analysis for the current IR unit, computing it if necessary.
template <typename AnalysisT> AnalysisT &getAnalysis(PassInstrumentor *pi) {
auto *id = AnalysisID::getID<AnalysisT>();
typename ConceptMap::iterator it;
bool wasInserted;
std::tie(it, wasInserted) = analyses.try_emplace(id);
// If we don't have a cached analysis for this function, compute it directly
// and add it to the cache.
if (wasInserted) {
if (pi)
pi->runBeforeAnalysis(getAnalysisName<AnalysisT>(), id, ir);
it->second = std::make_unique<AnalysisModel<AnalysisT>>(ir);
if (pi)
pi->runAfterAnalysis(getAnalysisName<AnalysisT>(), id, ir);
}
return static_cast<AnalysisModel<AnalysisT> &>(*it->second).analysis;
}
/// Get a cached analysis instance if one exists, otherwise return null.
template <typename AnalysisT>
Optional<std::reference_wrapper<AnalysisT>> getCachedAnalysis() const {
auto res = analyses.find(AnalysisID::getID<AnalysisT>());
if (res == analyses.end())
return llvm::None;
return {static_cast<AnalysisModel<AnalysisT> &>(*res->second).analysis};
}
/// Returns the operation that this analysis map represents.
Operation *getOperation() const { return ir; }
/// Clear any held analyses.
void clear() { analyses.clear(); }
/// Invalidate any cached analyses based upon the given set of preserved
/// analyses.
void invalidate(const PreservedAnalyses &pa) {
// Remove any analyses that were invalidated.
for (auto it = analyses.begin(), e = analyses.end(); it != e;) {
auto curIt = it++;
if (curIt->second->isInvalidated(pa))
analyses.erase(curIt);
}
}
private:
Operation *ir;
ConceptMap analyses;
};
/// An analysis map that contains a map for the current operation, and a set of
/// maps for any child operations.
struct NestedAnalysisMap {
NestedAnalysisMap(Operation *op) : analyses(op) {}
/// Get the operation for this analysis map.
Operation *getOperation() const { return analyses.getOperation(); }
/// Invalidate any non preserved analyses.
void invalidate(const PreservedAnalyses &pa);
/// The cached analyses for nested operations.
DenseMap<Operation *, std::unique_ptr<NestedAnalysisMap>> childAnalyses;
/// The analyses for the owning module.
detail::AnalysisMap analyses;
};
} // namespace detail
//===----------------------------------------------------------------------===//
// Analysis Management
//===----------------------------------------------------------------------===//
class ModuleAnalysisManager;
/// This class represents an analysis manager for a particular operation
/// instance. It is used to manage and cache analyses on the operation as well
/// as those for child operations, via nested AnalysisManager instances
/// accessible via 'slice'. This class is intended to be passed around by value,
/// and cannot be constructed directly.
class AnalysisManager {
using ParentPointerT =
PointerUnion<const ModuleAnalysisManager *, const AnalysisManager *>;
public:
using PreservedAnalyses = detail::PreservedAnalyses;
// Query for a cached analysis on the given parent operation. The analysis may
// not exist and if it does it may be out-of-date.
template <typename AnalysisT>
Optional<std::reference_wrapper<AnalysisT>>
getCachedParentAnalysis(Operation *parentOp) const {
ParentPointerT curParent = parent;
while (auto *parentAM = curParent.dyn_cast<const AnalysisManager *>()) {
if (parentAM->impl->getOperation() == parentOp)
return parentAM->getCachedAnalysis<AnalysisT>();
curParent = parentAM->parent;
}
return None;
}
// Query for the given analysis for the current operation.
template <typename AnalysisT> AnalysisT &getAnalysis() {
return impl->analyses.getAnalysis<AnalysisT>(getPassInstrumentor());
}
// Query for a cached entry of the given analysis on the current operation.
template <typename AnalysisT>
Optional<std::reference_wrapper<AnalysisT>> getCachedAnalysis() const {
return impl->analyses.getCachedAnalysis<AnalysisT>();
}
/// Query for a analysis of a child operation, constructing it if necessary.
template <typename AnalysisT> AnalysisT &getChildAnalysis(Operation *op) {
return slice(op).template getAnalysis<AnalysisT>();
}
/// Query for a cached analysis of a child operation, or return null.
template <typename AnalysisT>
Optional<std::reference_wrapper<AnalysisT>>
getCachedChildAnalysis(Operation *op) const {
assert(op->getParentOp() == impl->getOperation());
auto it = impl->childAnalyses.find(op);
if (it == impl->childAnalyses.end())
return llvm::None;
return it->second->analyses.getCachedAnalysis<AnalysisT>();
}
/// Get an analysis manager for the given child operation.
AnalysisManager slice(Operation *op);
/// Invalidate any non preserved analyses,
void invalidate(const PreservedAnalyses &pa) { impl->invalidate(pa); }
/// Clear any held analyses.
void clear() {
impl->analyses.clear();
impl->childAnalyses.clear();
}
/// Returns a pass instrumentation object for the current operation. This
/// value may be null.
PassInstrumentor *getPassInstrumentor() const;
private:
AnalysisManager(const AnalysisManager *parent,
detail::NestedAnalysisMap *impl)
: parent(parent), impl(impl) {}
AnalysisManager(const ModuleAnalysisManager *parent,
detail::NestedAnalysisMap *impl)
: parent(parent), impl(impl) {}
/// A reference to the parent analysis manager, or the top-level module
/// analysis manager.
ParentPointerT parent;
/// A reference to the impl analysis map within the parent analysis manager.
detail::NestedAnalysisMap *impl;
/// Allow access to the constructor.
friend class ModuleAnalysisManager;
};
/// An analysis manager class specifically for the top-level module operation.
/// This class contains the memory allocations for all nested analysis managers,
/// and provides an anchor point. This is necessary because AnalysisManager is
/// designed to be a thin wrapper around an existing analysis map instance.
class ModuleAnalysisManager {
public:
ModuleAnalysisManager(ModuleOp module, PassInstrumentor *passInstrumentor)
: analyses(module), passInstrumentor(passInstrumentor) {}
ModuleAnalysisManager(const ModuleAnalysisManager &) = delete;
ModuleAnalysisManager &operator=(const ModuleAnalysisManager &) = delete;
/// Returns a pass instrumentation object for the current module. This value
/// may be null.
PassInstrumentor *getPassInstrumentor() const { return passInstrumentor; }
/// Returns an analysis manager for the current top-level module.
operator AnalysisManager() { return AnalysisManager(this, &analyses); }
private:
/// The analyses for the owning module.
detail::NestedAnalysisMap analyses;
/// An optional instrumentation object.
PassInstrumentor *passInstrumentor;
};
} // end namespace mlir
#endif // MLIR_PASS_ANALYSISMANAGER_H