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