MLIR  17.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 /// Implementation of 'isInvalidated' if the analysis provides a definition.
89 template <typename AnalysisT>
90 std::enable_if_t<llvm::is_detected<has_is_invalidated, AnalysisT>::value, bool>
91 isInvalidated(AnalysisT &analysis, const PreservedAnalyses &pa) {
92  return analysis.isInvalidated(pa);
93 }
94 /// Default implementation of 'isInvalidated'.
95 template <typename AnalysisT>
96 std::enable_if_t<!llvm::is_detected<has_is_invalidated, AnalysisT>::value, bool>
97 isInvalidated(AnalysisT &analysis, const PreservedAnalyses &pa) {
98  return !pa.isPreserved<AnalysisT>();
99 }
100 } // namespace analysis_impl
101 
102 /// The abstract polymorphic base class representing an analysis.
104  virtual ~AnalysisConcept() = default;
105 
106  /// A hook used to query analyses for invalidation. Given a preserved analysis
107  /// set, returns true if it should truly be invalidated. This allows for more
108  /// fine-tuned invalidation in cases where an analysis wasn't explicitly
109  /// marked preserved, but may be preserved(or invalidated) based upon other
110  /// properties such as analyses sets. Invalidated analyses must also be
111  /// removed from pa.
112  virtual bool invalidate(PreservedAnalyses &pa) = 0;
113 };
114 
115 /// A derived analysis model used to hold a specific analysis object.
116 template <typename AnalysisT>
118  template <typename... Args>
119  explicit AnalysisModel(Args &&...args)
120  : analysis(std::forward<Args>(args)...) {}
121 
122  /// A hook used to query analyses for invalidation. Removes invalidated
123  /// analyses from pa.
124  bool invalidate(PreservedAnalyses &pa) final {
125  bool result = analysis_impl::isInvalidated(analysis, pa);
126  if (result)
127  pa.unpreserve<AnalysisT>();
128  return result;
129  }
130 
131  /// The actual analysis object.
132  AnalysisT analysis;
133 };
134 
135 /// This class represents a cache of analyses for a single operation. All
136 /// computation, caching, and invalidation of analyses takes place here.
137 class AnalysisMap {
138  /// A mapping between an analysis id and an existing analysis instance.
139  using ConceptMap = llvm::MapVector<TypeID, std::unique_ptr<AnalysisConcept>>;
140 
141  /// Utility to return the name of the given analysis class.
142  template <typename AnalysisT>
143  static StringRef getAnalysisName() {
144  StringRef name = llvm::getTypeName<AnalysisT>();
145  if (!name.consume_front("mlir::"))
146  name.consume_front("(anonymous namespace)::");
147  return name;
148  }
149 
150 public:
151  explicit AnalysisMap(Operation *ir) : ir(ir) {}
152 
153  /// Get an analysis for the current IR unit, computing it if necessary.
154  template <typename AnalysisT>
156  return getAnalysisImpl<AnalysisT, Operation *>(pi, ir, am);
157  }
158 
159  /// Get an analysis for the current IR unit assuming it's of specific derived
160  /// operation type.
161  template <typename AnalysisT, typename OpT>
162  std::enable_if_t<
163  std::is_constructible<AnalysisT, OpT>::value ||
164  std::is_constructible<AnalysisT, OpT, AnalysisManager &>::value,
165  AnalysisT &>
167  return getAnalysisImpl<AnalysisT, OpT>(pi, cast<OpT>(ir), am);
168  }
169 
170  /// Get a cached analysis instance if one exists, otherwise return null.
171  template <typename AnalysisT>
172  std::optional<std::reference_wrapper<AnalysisT>> getCachedAnalysis() const {
173  auto res = analyses.find(TypeID::get<AnalysisT>());
174  if (res == analyses.end())
175  return std::nullopt;
176  return {static_cast<AnalysisModel<AnalysisT> &>(*res->second).analysis};
177  }
178 
179  /// Returns the operation that this analysis map represents.
180  Operation *getOperation() const { return ir; }
181 
182  /// Clear any held analyses.
183  void clear() { analyses.clear(); }
184 
185  /// Invalidate any cached analyses based upon the given set of preserved
186  /// analyses.
187  void invalidate(const PreservedAnalyses &pa) {
188  PreservedAnalyses paCopy(pa);
189  // Remove any analyses that were invalidated.
190  // As we are using MapVector, order of insertion is preserved and
191  // dependencies always go before users, so we need only one iteration.
192  analyses.remove_if(
193  [&](auto &val) { return val.second->invalidate(paCopy); });
194  }
195 
196 private:
197  template <typename AnalysisT, typename OpT>
198  AnalysisT &getAnalysisImpl(PassInstrumentor *pi, OpT op,
199  AnalysisManager &am) {
200  TypeID id = TypeID::get<AnalysisT>();
201 
202  auto it = analyses.find(id);
203  // If we don't have a cached analysis for this operation, compute it
204  // directly and add it to the cache.
205  if (analyses.end() == it) {
206  if (pi)
207  pi->runBeforeAnalysis(getAnalysisName<AnalysisT>(), id, ir);
208 
209  bool wasInserted;
210  std::tie(it, wasInserted) =
211  analyses.insert({id, constructAnalysis<AnalysisT>(am, op)});
212  assert(wasInserted);
213 
214  if (pi)
215  pi->runAfterAnalysis(getAnalysisName<AnalysisT>(), id, ir);
216  }
217  return static_cast<AnalysisModel<AnalysisT> &>(*it->second).analysis;
218  }
219 
220  /// Construct analysis using two arguments constructor (OpT, AnalysisManager)
221  template <typename AnalysisT, typename OpT,
222  std::enable_if_t<std::is_constructible<
223  AnalysisT, OpT, AnalysisManager &>::value> * = nullptr>
224  static auto constructAnalysis(AnalysisManager &am, OpT op) {
225  return std::make_unique<AnalysisModel<AnalysisT>>(op, am);
226  }
227 
228  /// Construct analysis using single argument constructor (OpT)
229  template <typename AnalysisT, typename OpT,
230  std::enable_if_t<!std::is_constructible<
231  AnalysisT, OpT, AnalysisManager &>::value> * = nullptr>
232  static auto constructAnalysis(AnalysisManager &, OpT op) {
233  return std::make_unique<AnalysisModel<AnalysisT>>(op);
234  }
235 
236  Operation *ir;
237  ConceptMap analyses;
238 };
239 
240 /// An analysis map that contains a map for the current operation, and a set of
241 /// maps for any child operations.
244  : analyses(op), parentOrInstrumentor(instrumentor) {}
246  : analyses(op), parentOrInstrumentor(parent) {}
247 
248  /// Get the operation for this analysis map.
250 
251  /// Invalidate any non preserved analyses.
252  void invalidate(const PreservedAnalyses &pa);
253 
254  /// Returns the parent analysis map for this analysis map, or null if this is
255  /// the top-level map.
256  const NestedAnalysisMap *getParent() const {
257  return parentOrInstrumentor.dyn_cast<NestedAnalysisMap *>();
258  }
259 
260  /// Returns a pass instrumentation object for the current operation. This
261  /// value may be null.
263  if (auto *parent = getParent())
264  return parent->getPassInstrumentor();
265  return parentOrInstrumentor.get<PassInstrumentor *>();
266  }
267 
268  /// The cached analyses for nested operations.
270 
271  /// The analyses for the owning operation.
273 
274  /// This value has three possible states:
275  /// NestedAnalysisMap*: A pointer to the parent analysis map.
276  /// PassInstrumentor*: This analysis map is the top-level map, and this
277  /// pointer is the optional pass instrumentor for the
278  /// current compilation.
279  /// nullptr: This analysis map is the top-level map, and there is nop pass
280  /// instrumentor.
282 };
283 } // namespace detail
284 
285 //===----------------------------------------------------------------------===//
286 // Analysis Management
287 //===----------------------------------------------------------------------===//
289 
290 /// This class represents an analysis manager for a particular operation
291 /// instance. It is used to manage and cache analyses on the operation as well
292 /// as those for child operations, via nested AnalysisManager instances
293 /// accessible via 'slice'. This class is intended to be passed around by value,
294 /// and cannot be constructed directly.
296  using ParentPointerT =
298 
299 public:
301 
302  /// Query for a cached analysis on the given parent operation. The analysis
303  /// may not exist and if it does it may be out-of-date.
304  template <typename AnalysisT>
305  std::optional<std::reference_wrapper<AnalysisT>>
307  const detail::NestedAnalysisMap *curParent = impl;
308  while (auto *parentAM = curParent->getParent()) {
309  if (parentAM->getOperation() == parentOp)
310  return parentAM->analyses.getCachedAnalysis<AnalysisT>();
311  curParent = parentAM;
312  }
313  return std::nullopt;
314  }
315 
316  /// Query for the given analysis for the current operation.
317  template <typename AnalysisT>
318  AnalysisT &getAnalysis() {
319  return impl->analyses.getAnalysis<AnalysisT>(getPassInstrumentor(), *this);
320  }
321 
322  /// Query for the given analysis for the current operation of a specific
323  /// derived operation type.
324  template <typename AnalysisT, typename OpT>
325  AnalysisT &getAnalysis() {
326  return impl->analyses.getAnalysis<AnalysisT, OpT>(getPassInstrumentor(),
327  *this);
328  }
329 
330  /// Query for a cached entry of the given analysis on the current operation.
331  template <typename AnalysisT>
332  std::optional<std::reference_wrapper<AnalysisT>> getCachedAnalysis() const {
333  return impl->analyses.getCachedAnalysis<AnalysisT>();
334  }
335 
336  /// Query for an analysis of a child operation, constructing it if necessary.
337  template <typename AnalysisT>
338  AnalysisT &getChildAnalysis(Operation *op) {
339  return nest(op).template getAnalysis<AnalysisT>();
340  }
341 
342  /// Query for an analysis of a child operation of a specific derived operation
343  /// type, constructing it if necessary.
344  template <typename AnalysisT, typename OpT>
345  AnalysisT &getChildAnalysis(OpT child) {
346  return nest(child).template getAnalysis<AnalysisT, OpT>();
347  }
348 
349  /// Query for a cached analysis of a child operation, or return null.
350  template <typename AnalysisT>
351  std::optional<std::reference_wrapper<AnalysisT>>
353  assert(op->getParentOp() == impl->getOperation());
354  auto it = impl->childAnalyses.find(op);
355  if (it == impl->childAnalyses.end())
356  return std::nullopt;
357  return it->second->analyses.getCachedAnalysis<AnalysisT>();
358  }
359 
360  /// Get an analysis manager for the given operation, which must be a proper
361  /// descendant of the current operation represented by this analysis manager.
363 
364  /// Invalidate any non preserved analyses,
365  void invalidate(const PreservedAnalyses &pa) { impl->invalidate(pa); }
366 
367  /// Clear any held analyses.
368  void clear() {
369  impl->analyses.clear();
370  impl->childAnalyses.clear();
371  }
372 
373  /// Returns a pass instrumentation object for the current operation. This
374  /// value may be null.
376  return impl->getPassInstrumentor();
377  }
378 
379 private:
381 
382  /// Get an analysis manager for the given immediately nested child operation.
383  AnalysisManager nestImmediate(Operation *op);
384 
385  /// A reference to the impl analysis map within the parent analysis manager.
386  detail::NestedAnalysisMap *impl;
387 
388  /// Allow access to the constructor.
389  friend class ModuleAnalysisManager;
390 };
391 
392 /// An analysis manager class specifically for the top-level operation. This
393 /// class contains the memory allocations for all nested analysis managers, and
394 /// provides an anchor point. This is necessary because AnalysisManager is
395 /// designed to be a thin wrapper around an existing analysis map instance.
397 public:
399  : analyses(op, passInstrumentor) {}
402 
403  /// Returns an analysis manager for the current top-level module.
404  operator AnalysisManager() { return AnalysisManager(&analyses); }
405 
406 private:
407  /// The analyses for the owning module.
408  detail::NestedAnalysisMap analyses;
409 };
410 
411 } // namespace mlir
412 
413 #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:853
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:75
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition: Operation.h:209
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:988
void runAfterAnalysis(StringRef name, TypeID id, Operation *op)
See PassInstrumentation::runAfterAnalysis for details.
Definition: Pass.cpp:996
This class provides an efficient unique identifier for a specific C++ type.
Definition: TypeID.h:104
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.
std::enable_if_t< llvm::is_detected< has_is_invalidated, AnalysisT >::value, bool > isInvalidated(AnalysisT &analysis, const PreservedAnalyses &pa)
Implementation of 'isInvalidated' if the analysis provides a definition.
decltype(std::declval< T & >().isInvalidated(std::declval< const PreservedAnalyses & >())) has_is_invalidated
Trait to check if T provides a static 'isInvalidated' method.
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:889
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.