MLIR  14.0.0git
AnalysisManager.h
Go to the documentation of this file.
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"
14 #include "mlir/Support/LLVM.h"
15 #include "llvm/ADT/DenseMap.h"
16 #include "llvm/ADT/MapVector.h"
17 #include "llvm/ADT/SmallPtrSet.h"
18 #include "llvm/Support/TypeName.h"
19 
20 namespace mlir {
21 class AnalysisManager;
22 
23 //===----------------------------------------------------------------------===//
24 // Analysis Preservation and Concept Modeling
25 //===----------------------------------------------------------------------===//
26 
27 namespace detail {
28 /// A utility class to represent the analyses that are known to be preserved.
30  /// A type used to represent all potential analyses.
31  struct AllAnalysesType;
32 
33 public:
34  /// Mark all analyses as preserved.
35  void preserveAll() { preservedIDs.insert(TypeID::get<AllAnalysesType>()); }
36 
37  /// Returns true if all analyses were marked preserved.
38  bool isAll() const {
39  return preservedIDs.count(TypeID::get<AllAnalysesType>());
40  }
41 
42  /// Returns true if no analyses were marked preserved.
43  bool isNone() const { return preservedIDs.empty(); }
44 
45  /// Preserve the given analyses.
46  template <typename AnalysisT> void preserve() {
47  preserve(TypeID::get<AnalysisT>());
48  }
49  template <typename AnalysisT, typename AnalysisT2, typename... OtherAnalysesT>
50  void preserve() {
51  preserve<AnalysisT>();
52  preserve<AnalysisT2, OtherAnalysesT...>();
53  }
54  void preserve(TypeID id) { preservedIDs.insert(id); }
55 
56  /// Returns true if the given analysis has been marked as preserved. Note that
57  /// this simply checks for the presence of a given analysis ID and should not
58  /// be used as a general preservation checker.
59  template <typename AnalysisT> bool isPreserved() const {
60  return isPreserved(TypeID::get<AnalysisT>());
61  }
62  bool isPreserved(TypeID id) const { return preservedIDs.count(id); }
63 
64 private:
65  /// Remove the analysis from preserved set.
66  template <typename AnalysisT>
67  void unpreserve() {
68  preservedIDs.erase(TypeID::get<AnalysisT>());
69  }
70 
71  /// AnalysisModel need access to unpreserve().
72  template <typename>
73  friend struct AnalysisModel;
74 
75  /// The set of analyses that are known to be preserved.
76  SmallPtrSet<TypeID, 2> preservedIDs;
77 };
78 
79 namespace analysis_impl {
80 /// Trait to check if T provides a static 'isInvalidated' method.
81 template <typename T, typename... Args>
82 using has_is_invalidated = decltype(std::declval<T &>().isInvalidated(
83  std::declval<const PreservedAnalyses &>()));
84 
85 /// Implementation of 'isInvalidated' if the analysis provides a definition.
86 template <typename AnalysisT>
88 isInvalidated(AnalysisT &analysis, const PreservedAnalyses &pa) {
89  return analysis.isInvalidated(pa);
90 }
91 /// Default implementation of 'isInvalidated'.
92 template <typename AnalysisT>
94 isInvalidated(AnalysisT &analysis, const PreservedAnalyses &pa) {
95  return !pa.isPreserved<AnalysisT>();
96 }
97 } // namespace analysis_impl
98 
99 /// The abstract polymorphic base class representing an analysis.
101  virtual ~AnalysisConcept() = default;
102 
103  /// A hook used to query analyses for invalidation. Given a preserved analysis
104  /// set, returns true if it should truly be invalidated. This allows for more
105  /// fine-tuned invalidation in cases where an analysis wasn't explicitly
106  /// marked preserved, but may be preserved(or invalidated) based upon other
107  /// properties such as analyses sets. Invalidated analyses must also be
108  /// removed from pa.
109  virtual bool invalidate(PreservedAnalyses &pa) = 0;
110 };
111 
112 /// A derived analysis model used to hold a specific analysis object.
113 template <typename AnalysisT> struct AnalysisModel : public AnalysisConcept {
114  template <typename... Args>
115  explicit AnalysisModel(Args &&...args)
116  : analysis(std::forward<Args>(args)...) {}
117 
118  /// A hook used to query analyses for invalidation. Removes invalidated
119  /// analyses from pa.
120  bool invalidate(PreservedAnalyses &pa) final {
121  bool result = analysis_impl::isInvalidated(analysis, pa);
122  if (result)
123  pa.unpreserve<AnalysisT>();
124  return result;
125  }
126 
127  /// The actual analysis object.
128  AnalysisT analysis;
129 };
130 
131 /// This class represents a cache of analyses for a single operation. All
132 /// computation, caching, and invalidation of analyses takes place here.
133 class AnalysisMap {
134  /// A mapping between an analysis id and an existing analysis instance.
135  using ConceptMap = llvm::MapVector<TypeID, std::unique_ptr<AnalysisConcept>>;
136 
137  /// Utility to return the name of the given analysis class.
138  template <typename AnalysisT> static StringRef getAnalysisName() {
139  StringRef name = llvm::getTypeName<AnalysisT>();
140  if (!name.consume_front("mlir::"))
141  name.consume_front("(anonymous namespace)::");
142  return name;
143  }
144 
145 public:
146  explicit AnalysisMap(Operation *ir) : ir(ir) {}
147 
148  /// Get an analysis for the current IR unit, computing it if necessary.
149  template <typename AnalysisT>
151  return getAnalysisImpl<AnalysisT, Operation *>(pi, ir, am);
152  }
153 
154  /// Get an analysis for the current IR unit assuming it's of specific derived
155  /// operation type.
156  template <typename AnalysisT, typename OpT>
157  std::enable_if_t<
160  AnalysisT &>
162  return getAnalysisImpl<AnalysisT, OpT>(pi, cast<OpT>(ir), am);
163  }
164 
165  /// Get a cached analysis instance if one exists, otherwise return null.
166  template <typename AnalysisT>
168  auto res = analyses.find(TypeID::get<AnalysisT>());
169  if (res == analyses.end())
170  return llvm::None;
171  return {static_cast<AnalysisModel<AnalysisT> &>(*res->second).analysis};
172  }
173 
174  /// Returns the operation that this analysis map represents.
175  Operation *getOperation() const { return ir; }
176 
177  /// Clear any held analyses.
178  void clear() { analyses.clear(); }
179 
180  /// Invalidate any cached analyses based upon the given set of preserved
181  /// analyses.
182  void invalidate(const PreservedAnalyses &pa) {
183  PreservedAnalyses paCopy(pa);
184  // Remove any analyses that were invalidated.
185  // As we are using MapVector, order of insertion is preserved and
186  // dependencies always go before users, so we need only one iteration.
187  analyses.remove_if(
188  [&](auto &val) { return val.second->invalidate(paCopy); });
189  }
190 
191 private:
192  template <typename AnalysisT, typename OpT>
193  AnalysisT &getAnalysisImpl(PassInstrumentor *pi, OpT op,
194  AnalysisManager &am) {
195  TypeID id = TypeID::get<AnalysisT>();
196 
197  auto it = analyses.find(id);
198  // If we don't have a cached analysis for this operation, compute it
199  // directly and add it to the cache.
200  if (analyses.end() == it) {
201  if (pi)
202  pi->runBeforeAnalysis(getAnalysisName<AnalysisT>(), id, ir);
203 
204  bool wasInserted;
205  std::tie(it, wasInserted) =
206  analyses.insert({id, constructAnalysis<AnalysisT>(am, op)});
207  assert(wasInserted);
208 
209  if (pi)
210  pi->runAfterAnalysis(getAnalysisName<AnalysisT>(), id, ir);
211  }
212  return static_cast<AnalysisModel<AnalysisT> &>(*it->second).analysis;
213  }
214 
215  /// Construct analysis using two arguments constructor (OpT, AnalysisManager)
216  template <typename AnalysisT, typename OpT,
217  std::enable_if_t<std::is_constructible<
218  AnalysisT, OpT, AnalysisManager &>::value> * = nullptr>
219  static auto constructAnalysis(AnalysisManager &am, OpT op) {
220  return std::make_unique<AnalysisModel<AnalysisT>>(op, am);
221  }
222 
223  /// Construct analysis using single argument constructor (OpT)
224  template <typename AnalysisT, typename OpT,
225  std::enable_if_t<!std::is_constructible<
226  AnalysisT, OpT, AnalysisManager &>::value> * = nullptr>
227  static auto constructAnalysis(AnalysisManager &, OpT op) {
228  return std::make_unique<AnalysisModel<AnalysisT>>(op);
229  }
230 
231  Operation *ir;
232  ConceptMap analyses;
233 };
234 
235 /// An analysis map that contains a map for the current operation, and a set of
236 /// maps for any child operations.
239  : analyses(op), parentOrInstrumentor(instrumentor) {}
241  : analyses(op), parentOrInstrumentor(parent) {}
242 
243  /// Get the operation for this analysis map.
244  Operation *getOperation() const { return analyses.getOperation(); }
245 
246  /// Invalidate any non preserved analyses.
247  void invalidate(const PreservedAnalyses &pa);
248 
249  /// Returns the parent analysis map for this analysis map, or null if this is
250  /// the top-level map.
251  const NestedAnalysisMap *getParent() const {
252  return parentOrInstrumentor.dyn_cast<NestedAnalysisMap *>();
253  }
254 
255  /// Returns a pass instrumentation object for the current operation. This
256  /// value may be null.
258  if (auto *parent = getParent())
259  return parent->getPassInstrumentor();
260  return parentOrInstrumentor.get<PassInstrumentor *>();
261  }
262 
263  /// The cached analyses for nested operations.
265 
266  /// The analyses for the owning operation.
268 
269  /// This value has three possible states:
270  /// NestedAnalysisMap*: A pointer to the parent analysis map.
271  /// PassInstrumentor*: This analysis map is the top-level map, and this
272  /// pointer is the optional pass instrumentor for the
273  /// current compilation.
274  /// nullptr: This analysis map is the top-level map, and there is nop pass
275  /// instrumentor.
277 };
278 } // namespace detail
279 
280 //===----------------------------------------------------------------------===//
281 // Analysis Management
282 //===----------------------------------------------------------------------===//
284 
285 /// This class represents an analysis manager for a particular operation
286 /// instance. It is used to manage and cache analyses on the operation as well
287 /// as those for child operations, via nested AnalysisManager instances
288 /// accessible via 'slice'. This class is intended to be passed around by value,
289 /// and cannot be constructed directly.
291  using ParentPointerT =
293 
294 public:
296 
297  /// Query for a cached analysis on the given parent operation. The analysis
298  /// may not exist and if it does it may be out-of-date.
299  template <typename AnalysisT>
302  const detail::NestedAnalysisMap *curParent = impl;
303  while (auto *parentAM = curParent->getParent()) {
304  if (parentAM->getOperation() == parentOp)
305  return parentAM->analyses.getCachedAnalysis<AnalysisT>();
306  curParent = parentAM;
307  }
308  return None;
309  }
310 
311  /// Query for the given analysis for the current operation.
312  template <typename AnalysisT> AnalysisT &getAnalysis() {
313  return impl->analyses.getAnalysis<AnalysisT>(getPassInstrumentor(), *this);
314  }
315 
316  /// Query for the given analysis for the current operation of a specific
317  /// derived operation type.
318  template <typename AnalysisT, typename OpT>
319  AnalysisT &getAnalysis() {
320  return impl->analyses.getAnalysis<AnalysisT, OpT>(getPassInstrumentor(),
321  *this);
322  }
323 
324  /// Query for a cached entry of the given analysis on the current operation.
325  template <typename AnalysisT>
327  return impl->analyses.getCachedAnalysis<AnalysisT>();
328  }
329 
330  /// Query for an analysis of a child operation, constructing it if necessary.
331  template <typename AnalysisT> AnalysisT &getChildAnalysis(Operation *op) {
332  return nest(op).template getAnalysis<AnalysisT>();
333  }
334 
335  /// Query for an analysis of a child operation of a specific derived operation
336  /// type, constructing it if necessary.
337  template <typename AnalysisT, typename OpT>
338  AnalysisT &getChildAnalysis(OpT child) {
339  return nest(child).template getAnalysis<AnalysisT, OpT>();
340  }
341 
342  /// Query for a cached analysis of a child operation, or return null.
343  template <typename AnalysisT>
346  assert(op->getParentOp() == impl->getOperation());
347  auto it = impl->childAnalyses.find(op);
348  if (it == impl->childAnalyses.end())
349  return llvm::None;
350  return it->second->analyses.getCachedAnalysis<AnalysisT>();
351  }
352 
353  /// Get an analysis manager for the given operation, which must be a proper
354  /// descendant of the current operation represented by this analysis manager.
355  AnalysisManager nest(Operation *op);
356 
357  /// Invalidate any non preserved analyses,
358  void invalidate(const PreservedAnalyses &pa) { impl->invalidate(pa); }
359 
360  /// Clear any held analyses.
361  void clear() {
362  impl->analyses.clear();
363  impl->childAnalyses.clear();
364  }
365 
366  /// Returns a pass instrumentation object for the current operation. This
367  /// value may be null.
369  return impl->getPassInstrumentor();
370  }
371 
372 private:
374 
375  /// Get an analysis manager for the given immediately nested child operation.
376  AnalysisManager nestImmediate(Operation *op);
377 
378  /// A reference to the impl analysis map within the parent analysis manager.
380 
381  /// Allow access to the constructor.
382  friend class ModuleAnalysisManager;
383 };
384 
385 /// An analysis manager class specifically for the top-level operation. This
386 /// class contains the memory allocations for all nested analysis managers, and
387 /// provides an anchor point. This is necessary because AnalysisManager is
388 /// designed to be a thin wrapper around an existing analysis map instance.
390 public:
392  : analyses(op, passInstrumentor) {}
394  ModuleAnalysisManager &operator=(const ModuleAnalysisManager &) = delete;
395 
396  /// Returns an analysis manager for the current top-level module.
397  operator AnalysisManager() { return AnalysisManager(&analyses); }
398 
399 private:
400  /// The analyses for the owning module.
401  detail::NestedAnalysisMap analyses;
402 };
403 
404 } // namespace mlir
405 
406 #endif // MLIR_PASS_ANALYSISMANAGER_H
Include the generated interface declarations.
AnalysisT & getAnalysis()
Query for the given analysis for the current operation.
Operation is a basic unit of execution within MLIR.
Definition: Operation.h:28
bool isAll() const
Returns true if all analyses were marked preserved.
void runAfterAnalysis(StringRef name, TypeID id, Operation *op)
See PassInstrumentation::runAfterAnalysis for details.
Definition: Pass.cpp:843
PassInstrumentor * getPassInstrumentor() const
Returns a pass instrumentation object for the current operation.
This class represents an analysis manager for a particular operation instance.
PointerUnion< NestedAnalysisMap *, PassInstrumentor * > parentOrInstrumentor
This value has three possible states: NestedAnalysisMap*: A pointer to the parent analysis map...
void preserveAll()
Mark all analyses as preserved.
static constexpr const bool value
This class provides an efficient unique identifier for a specific C++ type.
Definition: TypeID.h:52
void invalidate(const PreservedAnalyses &pa)
Invalidate any cached analyses based upon the given set of preserved analyses.
bool invalidate(PreservedAnalyses &pa) final
A hook used to query analyses for invalidation.
PassInstrumentor * getPassInstrumentor() const
Returns a pass instrumentation object for the current operation.
An analysis manager class specifically for the top-level operation.
AnalysisT analysis
The actual analysis object.
The abstract polymorphic base class representing an analysis.
const NestedAnalysisMap * getParent() const
Returns the parent analysis map for this analysis map, or null if this is the top-level map...
decltype(std::declval< T & >().isInvalidated(std::declval< const PreservedAnalyses & >())) has_is_invalidated
Trait to check if T provides a static &#39;isInvalidated&#39; method.
ModuleAnalysisManager(Operation *op, PassInstrumentor *passInstrumentor)
Operation * getOperation() const
Get the operation for this analysis map.
This class represents a cache of analyses for a single operation.
detail::AnalysisMap analyses
The analyses for the owning operation.
NestedAnalysisMap(Operation *op, PassInstrumentor *instrumentor)
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition: Operation.h:117
Operation * getOperation() const
Returns the operation that this analysis map represents.
std::enable_if_t< std::is_constructible< AnalysisT, OpT >::value||std::is_constructible< AnalysisT, OpT, AnalysisManager & >::value, AnalysisT & > getAnalysis(PassInstrumentor *pi, AnalysisManager &am)
Get an analysis for the current IR unit assuming it&#39;s of specific derived operation type...
std::enable_if_t<!llvm::is_detected< has_is_invalidated, AnalysisT >::value, bool > isInvalidated(AnalysisT &analysis, const PreservedAnalyses &pa)
Default implementation of &#39;isInvalidated&#39;.
A utility class to represent the analyses that are known to be preserved.
void runBeforeAnalysis(StringRef name, TypeID id, Operation *op)
See PassInstrumentation::runBeforeAnalysis for details.
Definition: Pass.cpp:835
bool isPreserved() const
Returns true if the given analysis has been marked as preserved.
Optional< std::reference_wrapper< AnalysisT > > getCachedChildAnalysis(Operation *op) const
Query for a cached analysis of a child operation, or return null.
void invalidate(const PreservedAnalyses &pa)
Invalidate any non preserved analyses,.
NestedAnalysisMap(Operation *op, NestedAnalysisMap *parent)
This class holds a collection of PassInstrumentation objects, and invokes their respective call backs...
std::enable_if_t< llvm::is_detected< has_is_invalidated, AnalysisT >::value, bool > isInvalidated(AnalysisT &analysis, const PreservedAnalyses &pa)
Implementation of &#39;isInvalidated&#39; if the analysis provides a definition.
AnalysisT & getAnalysis(PassInstrumentor *pi, AnalysisManager &am)
Get an analysis for the current IR unit, computing it if necessary.
AnalysisT & getChildAnalysis(OpT child)
Query for an analysis of a child operation of a specific derived operation type, constructing it if n...
DenseMap< Operation *, std::unique_ptr< NestedAnalysisMap > > childAnalyses
The cached analyses for nested operations.
An analysis map that contains a map for the current operation, and a set of maps for any child operat...
void clear()
Clear any held analyses.
AnalysisT & getChildAnalysis(Operation *op)
Query for an analysis of a child operation, constructing it if necessary.
AnalysisT & getAnalysis()
Query for the given analysis for the current operation of a specific derived operation type...
bool isPreserved(TypeID id) const
Optional< std::reference_wrapper< AnalysisT > > getCachedAnalysis() const
Query for a cached entry of the given analysis on the current operation.
Optional< std::reference_wrapper< AnalysisT > > getCachedAnalysis() const
Get a cached analysis instance if one exists, otherwise return null.
Optional< std::reference_wrapper< AnalysisT > > getCachedParentAnalysis(Operation *parentOp) const
Query for a cached analysis on the given parent operation.
void clear()
Clear any held analyses.
A derived analysis model used to hold a specific analysis object.
bool isNone() const
Returns true if no analyses were marked preserved.
void preserve()
Preserve the given analyses.