MLIR 22.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
21namespace mlir {
22class AnalysisManager;
23
24//===----------------------------------------------------------------------===//
25// Analysis Preservation and Concept Modeling
26//===----------------------------------------------------------------------===//
27
28namespace 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
34public:
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>
51 template <typename AnalysisT, typename AnalysisT2, typename... OtherAnalysesT>
52 void preserve() {
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 {
64 }
65 bool isPreserved(TypeID id) const { return preservedIDs.count(id); }
66
67private:
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
82namespace analysis_impl {
83/// Trait to check if T provides a static 'isInvalidated' method.
84template <typename T, typename... Args>
85using has_is_invalidated = decltype(std::declval<T &>().isInvalidated(
86 std::declval<const PreservedAnalyses &>()));
87
88template <typename AnalysisT>
89bool 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.
113template <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.
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.
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
147public:
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.
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
193private:
194 template <typename AnalysisT, typename OpT>
195 AnalysisT &getAnalysisImpl(PassInstrumentor *pi, OpT op,
196 AnalysisManager &am) {
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) {}
244
245 /// Get the operation for this analysis map.
246 Operation *getOperation() const { return analyses.getOperation(); }
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.
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.
292class AnalysisManager {
293 using ParentPointerT =
295
296public:
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
376private:
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.
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.
394public:
396 : analyses(op, passInstrumentor) {}
399
400 /// Returns an analysis manager for the current top-level module.
401 operator AnalysisManager() { return AnalysisManager(&analyses); }
402
403private:
404 /// The analyses for the owning module.
406};
407
408} // namespace mlir
409
410#endif // MLIR_PASS_ANALYSISMANAGER_H
This class represents an analysis manager for a particular operation instance.
AnalysisT & getAnalysis()
Query for the given analysis for the current operation of a specific derived operation type.
void clear()
Clear any held analyses.
std::optional< std::reference_wrapper< AnalysisT > > getCachedChildAnalysis(Operation *op) const
Query for a cached analysis of a child operation, or return null.
std::optional< std::reference_wrapper< AnalysisT > > getCachedParentAnalysis(Operation *parentOp) const
Query for a cached analysis on the given parent operation.
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:1127
friend class ModuleAnalysisManager
Allow access to the constructor.
PassInstrumentor * getPassInstrumentor() const
Returns a pass instrumentation object for the current operation.
AnalysisT & getChildAnalysis(Operation *op)
Query for an analysis of a child operation, constructing it if necessary.
detail::PreservedAnalyses PreservedAnalyses
AnalysisT & getChildAnalysis(OpT child)
Query for an analysis of a child operation of a specific derived operation type, constructing it if n...
AnalysisT & getAnalysis()
Query for the given analysis for the current operation.
std::optional< std::reference_wrapper< AnalysisT > > getCachedAnalysis() const
Query for a cached entry of the given analysis on the current operation.
An analysis manager class specifically for the top-level operation.
ModuleAnalysisManager(Operation *op, PassInstrumentor *passInstrumentor)
ModuleAnalysisManager(const ModuleAnalysisManager &)=delete
ModuleAnalysisManager & operator=(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:1260
void runAfterAnalysis(StringRef name, TypeID id, Operation *op)
See PassInstrumentation::runAfterAnalysis for details.
Definition Pass.cpp:1268
This class provides an efficient unique identifier for a specific C++ type.
Definition TypeID.h:107
static TypeID get()
Construct a type info object for the given type T.
Definition TypeID.h:245
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.
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.
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.
A utility class to represent the analyses that are known to be preserved.
friend struct AnalysisModel
AnalysisModel need access to unpreserve().
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.
bool isInvalidated(AnalysisT &analysis, const PreservedAnalyses &pa)
decltype(std::declval< T & >().isInvalidated( std::declval< const PreservedAnalyses & >())) has_is_invalidated
Trait to check if T provides a static 'isInvalidated' method.
AttrTypeReplacer.
Include the generated interface declarations.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:126
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.
const NestedAnalysisMap * getParent() const
Returns the parent analysis map for this analysis map, or null if this is the top-level map.
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:1161
Operation * getOperation() const
Get the operation for this analysis map.