1 //===- AnalysisManager.h - Analysis Management Infrastructure ---*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef MLIR_PASS_ANALYSISMANAGER_H
10 #define MLIR_PASS_ANALYSISMANAGER_H
11
12 #include "mlir/IR/Operation.h"
13 #include "mlir/Pass/PassInstrumentation.h"
14 #include "mlir/Support/LLVM.h"
15 #include "llvm/ADT/DenseMap.h"
16 #include "llvm/ADT/SmallPtrSet.h"
17 #include "llvm/Support/TypeName.h"
18
19 namespace mlir {
20 //===----------------------------------------------------------------------===//
21 // Analysis Preservation and Concept Modeling
22 //===----------------------------------------------------------------------===//
23
24 namespace detail {
25 /// A utility class to represent the analyses that are known to be preserved.
26 class PreservedAnalyses {
27 /// A type used to represent all potential analyses.
28 struct AllAnalysesType;
29
30 public:
31 /// Mark all analyses as preserved.
preserveAll()32 void preserveAll() { preservedIDs.insert(TypeID::get<AllAnalysesType>()); }
33
34 /// Returns true if all analyses were marked preserved.
isAll()35 bool isAll() const {
36 return preservedIDs.count(TypeID::get<AllAnalysesType>());
37 }
38
39 /// Returns true if no analyses were marked preserved.
isNone()40 bool isNone() const { return preservedIDs.empty(); }
41
42 /// Preserve the given analyses.
preserve()43 template <typename AnalysisT> void preserve() {
44 preserve(TypeID::get<AnalysisT>());
45 }
46 template <typename AnalysisT, typename AnalysisT2, typename... OtherAnalysesT>
preserve()47 void preserve() {
48 preserve<AnalysisT>();
49 preserve<AnalysisT2, OtherAnalysesT...>();
50 }
preserve(TypeID id)51 void preserve(TypeID id) { preservedIDs.insert(id); }
52
53 /// Returns true if the given analysis has been marked as preserved. Note that
54 /// this simply checks for the presence of a given analysis ID and should not
55 /// be used as a general preservation checker.
isPreserved()56 template <typename AnalysisT> bool isPreserved() const {
57 return isPreserved(TypeID::get<AnalysisT>());
58 }
isPreserved(TypeID id)59 bool isPreserved(TypeID id) const { return preservedIDs.count(id); }
60
61 private:
62 /// The set of analyses that are known to be preserved.
63 SmallPtrSet<TypeID, 2> preservedIDs;
64 };
65
66 namespace analysis_impl {
67 /// Trait to check if T provides a static 'isInvalidated' method.
68 template <typename T, typename... Args>
69 using has_is_invalidated = decltype(std::declval<T &>().isInvalidated(
70 std::declval<const PreservedAnalyses &>()));
71
72 /// Implementation of 'isInvalidated' if the analysis provides a definition.
73 template <typename AnalysisT>
74 std::enable_if_t<llvm::is_detected<has_is_invalidated, AnalysisT>::value, bool>
isInvalidated(AnalysisT & analysis,const PreservedAnalyses & pa)75 isInvalidated(AnalysisT &analysis, const PreservedAnalyses &pa) {
76 return analysis.isInvalidated(pa);
77 }
78 /// Default implementation of 'isInvalidated'.
79 template <typename AnalysisT>
80 std::enable_if_t<!llvm::is_detected<has_is_invalidated, AnalysisT>::value, bool>
isInvalidated(AnalysisT & analysis,const PreservedAnalyses & pa)81 isInvalidated(AnalysisT &analysis, const PreservedAnalyses &pa) {
82 return !pa.isPreserved<AnalysisT>();
83 }
84 } // end namespace analysis_impl
85
86 /// The abstract polymorphic base class representing an analysis.
87 struct AnalysisConcept {
88 virtual ~AnalysisConcept() = default;
89
90 /// A hook used to query analyses for invalidation. Given a preserved analysis
91 /// set, returns true if it should truly be invalidated. This allows for more
92 /// fine-tuned invalidation in cases where an analysis wasn't explicitly
93 /// marked preserved, but may be preserved(or invalidated) based upon other
94 /// properties such as analyses sets.
95 virtual bool isInvalidated(const PreservedAnalyses &pa) = 0;
96 };
97
98 /// A derived analysis model used to hold a specific analysis object.
99 template <typename AnalysisT> struct AnalysisModel : public AnalysisConcept {
100 template <typename... Args>
AnalysisModelAnalysisModel101 explicit AnalysisModel(Args &&... args)
102 : analysis(std::forward<Args>(args)...) {}
103
104 /// A hook used to query analyses for invalidation.
isInvalidatedAnalysisModel105 bool isInvalidated(const PreservedAnalyses &pa) final {
106 return analysis_impl::isInvalidated(analysis, pa);
107 }
108
109 /// The actual analysis object.
110 AnalysisT analysis;
111 };
112
113 /// This class represents a cache of analyses for a single operation. All
114 /// computation, caching, and invalidation of analyses takes place here.
115 class AnalysisMap {
116 /// A mapping between an analysis id and an existing analysis instance.
117 using ConceptMap = DenseMap<TypeID, std::unique_ptr<AnalysisConcept>>;
118
119 /// Utility to return the name of the given analysis class.
getAnalysisName()120 template <typename AnalysisT> static StringRef getAnalysisName() {
121 StringRef name = llvm::getTypeName<AnalysisT>();
122 if (!name.consume_front("mlir::"))
123 name.consume_front("(anonymous namespace)::");
124 return name;
125 }
126
127 public:
AnalysisMap(Operation * ir)128 explicit AnalysisMap(Operation *ir) : ir(ir) {}
129
130 /// Get an analysis for the current IR unit, computing it if necessary.
131 template <typename AnalysisT>
getAnalysis(PassInstrumentor * pi)132 AnalysisT &getAnalysis(PassInstrumentor *pi) {
133 return getAnalysisImpl<AnalysisT, Operation *>(pi, ir);
134 }
135
136 /// Get an analysis for the current IR unit assuming it's of specific derived
137 /// operation type.
138 template <typename AnalysisT, typename OpT>
139 typename std::enable_if<std::is_constructible<AnalysisT, OpT>::value,
140 AnalysisT &>::type
getAnalysis(PassInstrumentor * pi)141 getAnalysis(PassInstrumentor *pi) {
142 return getAnalysisImpl<AnalysisT, OpT>(pi, cast<OpT>(ir));
143 }
144
145 /// Get a cached analysis instance if one exists, otherwise return null.
146 template <typename AnalysisT>
getCachedAnalysis()147 Optional<std::reference_wrapper<AnalysisT>> getCachedAnalysis() const {
148 auto res = analyses.find(TypeID::get<AnalysisT>());
149 if (res == analyses.end())
150 return llvm::None;
151 return {static_cast<AnalysisModel<AnalysisT> &>(*res->second).analysis};
152 }
153
154 /// Returns the operation that this analysis map represents.
getOperation()155 Operation *getOperation() const { return ir; }
156
157 /// Clear any held analyses.
clear()158 void clear() { analyses.clear(); }
159
160 /// Invalidate any cached analyses based upon the given set of preserved
161 /// analyses.
invalidate(const PreservedAnalyses & pa)162 void invalidate(const PreservedAnalyses &pa) {
163 // Remove any analyses that were invalidated.
164 for (auto it = analyses.begin(), e = analyses.end(); it != e;) {
165 auto curIt = it++;
166 if (curIt->second->isInvalidated(pa))
167 analyses.erase(curIt);
168 }
169 }
170
171 private:
172 template <typename AnalysisT, typename OpT>
getAnalysisImpl(PassInstrumentor * pi,OpT op)173 AnalysisT &getAnalysisImpl(PassInstrumentor *pi, OpT op) {
174 TypeID id = TypeID::get<AnalysisT>();
175
176 typename ConceptMap::iterator it;
177 bool wasInserted;
178 std::tie(it, wasInserted) = analyses.try_emplace(id);
179
180 // If we don't have a cached analysis for this operation, compute it
181 // directly and add it to the cache.
182 if (wasInserted) {
183 if (pi)
184 pi->runBeforeAnalysis(getAnalysisName<AnalysisT>(), id, ir);
185
186 it->second = std::make_unique<AnalysisModel<AnalysisT>>(op);
187
188 if (pi)
189 pi->runAfterAnalysis(getAnalysisName<AnalysisT>(), id, ir);
190 }
191 return static_cast<AnalysisModel<AnalysisT> &>(*it->second).analysis;
192 }
193
194 Operation *ir;
195 ConceptMap analyses;
196 };
197
198 /// An analysis map that contains a map for the current operation, and a set of
199 /// maps for any child operations.
200 struct NestedAnalysisMap {
NestedAnalysisMapNestedAnalysisMap201 NestedAnalysisMap(Operation *op) : analyses(op) {}
202
203 /// Get the operation for this analysis map.
getOperationNestedAnalysisMap204 Operation *getOperation() const { return analyses.getOperation(); }
205
206 /// Invalidate any non preserved analyses.
207 void invalidate(const PreservedAnalyses &pa);
208
209 /// The cached analyses for nested operations.
210 DenseMap<Operation *, std::unique_ptr<NestedAnalysisMap>> childAnalyses;
211
212 /// The analyses for the owning module.
213 detail::AnalysisMap analyses;
214 };
215 } // namespace detail
216
217 //===----------------------------------------------------------------------===//
218 // Analysis Management
219 //===----------------------------------------------------------------------===//
220 class ModuleAnalysisManager;
221
222 /// This class represents an analysis manager for a particular operation
223 /// instance. It is used to manage and cache analyses on the operation as well
224 /// as those for child operations, via nested AnalysisManager instances
225 /// accessible via 'slice'. This class is intended to be passed around by value,
226 /// and cannot be constructed directly.
227 class AnalysisManager {
228 using ParentPointerT =
229 PointerUnion<const ModuleAnalysisManager *, const AnalysisManager *>;
230
231 public:
232 using PreservedAnalyses = detail::PreservedAnalyses;
233
234 /// Query for a cached analysis on the given parent operation. The analysis
235 /// may not exist and if it does it may be out-of-date.
236 template <typename AnalysisT>
237 Optional<std::reference_wrapper<AnalysisT>>
getCachedParentAnalysis(Operation * parentOp)238 getCachedParentAnalysis(Operation *parentOp) const {
239 ParentPointerT curParent = parent;
240 while (auto *parentAM = curParent.dyn_cast<const AnalysisManager *>()) {
241 if (parentAM->impl->getOperation() == parentOp)
242 return parentAM->getCachedAnalysis<AnalysisT>();
243 curParent = parentAM->parent;
244 }
245 return None;
246 }
247
248 /// Query for the given analysis for the current operation.
getAnalysis()249 template <typename AnalysisT> AnalysisT &getAnalysis() {
250 return impl->analyses.getAnalysis<AnalysisT>(getPassInstrumentor());
251 }
252
253 /// Query for the given analysis for the current operation of a specific
254 /// derived operation type.
255 template <typename AnalysisT, typename OpT>
getAnalysis()256 AnalysisT &getAnalysis() {
257 return impl->analyses.getAnalysis<AnalysisT, OpT>(getPassInstrumentor());
258 }
259
260 /// Query for a cached entry of the given analysis on the current operation.
261 template <typename AnalysisT>
getCachedAnalysis()262 Optional<std::reference_wrapper<AnalysisT>> getCachedAnalysis() const {
263 return impl->analyses.getCachedAnalysis<AnalysisT>();
264 }
265
266 /// Query for an analysis of a child operation, constructing it if necessary.
getChildAnalysis(Operation * op)267 template <typename AnalysisT> AnalysisT &getChildAnalysis(Operation *op) {
268 return nest(op).template getAnalysis<AnalysisT>();
269 }
270
271 /// Query for an analysis of a child operation of a specific derived operation
272 /// type, constructing it if necessary.
273 template <typename AnalysisT, typename OpT>
getChildAnalysis(OpT child)274 AnalysisT &getChildAnalysis(OpT child) {
275 return nest(child).template getAnalysis<AnalysisT, OpT>();
276 }
277
278 /// Query for a cached analysis of a child operation, or return null.
279 template <typename AnalysisT>
280 Optional<std::reference_wrapper<AnalysisT>>
getCachedChildAnalysis(Operation * op)281 getCachedChildAnalysis(Operation *op) const {
282 assert(op->getParentOp() == impl->getOperation());
283 auto it = impl->childAnalyses.find(op);
284 if (it == impl->childAnalyses.end())
285 return llvm::None;
286 return it->second->analyses.getCachedAnalysis<AnalysisT>();
287 }
288
289 /// Get an analysis manager for the given child operation.
290 AnalysisManager nest(Operation *op);
291
292 /// Invalidate any non preserved analyses,
invalidate(const PreservedAnalyses & pa)293 void invalidate(const PreservedAnalyses &pa) { impl->invalidate(pa); }
294
295 /// Clear any held analyses.
clear()296 void clear() {
297 impl->analyses.clear();
298 impl->childAnalyses.clear();
299 }
300
301 /// Returns a pass instrumentation object for the current operation. This
302 /// value may be null.
303 PassInstrumentor *getPassInstrumentor() const;
304
305 private:
AnalysisManager(const AnalysisManager * parent,detail::NestedAnalysisMap * impl)306 AnalysisManager(const AnalysisManager *parent,
307 detail::NestedAnalysisMap *impl)
308 : parent(parent), impl(impl) {}
AnalysisManager(const ModuleAnalysisManager * parent,detail::NestedAnalysisMap * impl)309 AnalysisManager(const ModuleAnalysisManager *parent,
310 detail::NestedAnalysisMap *impl)
311 : parent(parent), impl(impl) {}
312
313 /// A reference to the parent analysis manager, or the top-level module
314 /// analysis manager.
315 ParentPointerT parent;
316
317 /// A reference to the impl analysis map within the parent analysis manager.
318 detail::NestedAnalysisMap *impl;
319
320 /// Allow access to the constructor.
321 friend class ModuleAnalysisManager;
322 };
323
324 /// An analysis manager class specifically for the top-level operation. This
325 /// class contains the memory allocations for all nested analysis managers, and
326 /// provides an anchor point. This is necessary because AnalysisManager is
327 /// designed to be a thin wrapper around an existing analysis map instance.
328 class ModuleAnalysisManager {
329 public:
ModuleAnalysisManager(Operation * op,PassInstrumentor * passInstrumentor)330 ModuleAnalysisManager(Operation *op, PassInstrumentor *passInstrumentor)
331 : analyses(op), passInstrumentor(passInstrumentor) {}
332 ModuleAnalysisManager(const ModuleAnalysisManager &) = delete;
333 ModuleAnalysisManager &operator=(const ModuleAnalysisManager &) = delete;
334
335 /// Returns a pass instrumentation object for the current module. This value
336 /// may be null.
getPassInstrumentor()337 PassInstrumentor *getPassInstrumentor() const { return passInstrumentor; }
338
339 /// Returns an analysis manager for the current top-level module.
AnalysisManager()340 operator AnalysisManager() { return AnalysisManager(this, &analyses); }
341
342 private:
343 /// The analyses for the owning module.
344 detail::NestedAnalysisMap analyses;
345
346 /// An optional instrumentation object.
347 PassInstrumentor *passInstrumentor;
348 };
349
350 } // end namespace mlir
351
352 #endif // MLIR_PASS_ANALYSISMANAGER_H
353