• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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