MLIR  20.0.0git
DataFlowFramework.h
Go to the documentation of this file.
1 //===- DataFlowFramework.h - A generic framework for data-flow analysis ---===//
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 // This file defines a generic framework for writing data-flow analysis in MLIR.
10 // The framework consists of a solver, which runs the fixed-point iteration and
11 // manages analysis dependencies, and a data-flow analysis class used to
12 // implement specific analyses.
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #ifndef MLIR_ANALYSIS_DATAFLOWFRAMEWORK_H
17 #define MLIR_ANALYSIS_DATAFLOWFRAMEWORK_H
18 
19 #include "mlir/IR/Operation.h"
21 #include "llvm/ADT/SetVector.h"
22 #include "llvm/Support/Compiler.h"
23 #include "llvm/Support/TypeName.h"
24 #include <queue>
25 
26 namespace mlir {
27 
28 //===----------------------------------------------------------------------===//
29 // ChangeResult
30 //===----------------------------------------------------------------------===//
31 
32 /// A result type used to indicate if a change happened. Boolean operations on
33 /// ChangeResult behave as though `Change` is truth.
34 enum class [[nodiscard]] ChangeResult {
35  NoChange,
36  Change,
37 };
39  return lhs == ChangeResult::Change ? lhs : rhs;
40 }
42  lhs = lhs | rhs;
43  return lhs;
44 }
46  return lhs == ChangeResult::NoChange ? lhs : rhs;
47 }
48 
49 /// Forward declare the analysis state class.
50 class AnalysisState;
51 
52 /// Program point represents a specific location in the execution of a program.
53 /// A sequence of program points can be combined into a control flow graph.
54 struct ProgramPoint : public PointerUnion<Operation *, Block *> {
56  /// Inherit constructors.
57  using ParentTy::PointerUnion;
58  /// Allow implicit conversion from the parent type.
59  ProgramPoint(ParentTy point = nullptr) : ParentTy(point) {}
60  /// Allow implicit conversions from operation wrappers.
61  /// TODO: For Windows only. Find a better solution.
62  template <typename OpT, typename = std::enable_if_t<
63  std::is_convertible<OpT, Operation *>::value &&
64  !std::is_same<OpT, Operation *>::value>>
65  ProgramPoint(OpT op) : ParentTy(op) {}
66 
67  /// Print the program point.
68  void print(raw_ostream &os) const;
69 };
70 
71 //===----------------------------------------------------------------------===//
72 // GenericLatticeAnchor
73 //===----------------------------------------------------------------------===//
74 
75 /// Abstract class for generic lattice anchor. In classical data-flow analysis,
76 /// lattice anchor represent positions in a program to which lattice elements
77 /// are attached. In sparse data-flow analysis, these can be SSA values, and in
78 /// dense data-flow analysis, these are the program points before and after
79 /// every operation.
80 ///
81 /// Lattice anchor are implemented using MLIR's storage uniquer framework and
82 /// type ID system to provide RTTI.
84 public:
86 
87  /// Get the abstract lattice anchor's type identifier.
88  TypeID getTypeID() const { return typeID; }
89 
90  /// Get a derived source location for the lattice anchor.
91  virtual Location getLoc() const = 0;
92 
93  /// Print the lattice anchor.
94  virtual void print(raw_ostream &os) const = 0;
95 
96 protected:
97  /// Create an abstract lattice anchor with type identifier.
98  explicit GenericLatticeAnchor(TypeID typeID) : typeID(typeID) {}
99 
100 private:
101  /// The type identifier of the lattice anchor.
102  TypeID typeID;
103 };
104 
105 //===----------------------------------------------------------------------===//
106 // GenericLatticeAnchorBase
107 //===----------------------------------------------------------------------===//
108 
109 /// Base class for generic lattice anchor based on a concrete lattice anchor
110 /// type and a content key. This class defines the common methods required for
111 /// operability with the storage uniquer framework.
112 ///
113 /// The provided key type uniquely identifies the concrete lattice anchor
114 /// instance and are the data members of the class.
115 template <typename ConcreteT, typename Value>
117 public:
118  /// The concrete key type used by the storage uniquer. This class is uniqued
119  /// by its contents.
120  using KeyTy = Value;
121  /// Alias for the base class.
123 
124  /// Construct an instance of the lattice anchor using the provided value and
125  /// the type ID of the concrete type.
126  template <typename ValueT>
127  explicit GenericLatticeAnchorBase(ValueT &&value)
128  : GenericLatticeAnchor(TypeID::get<ConcreteT>()),
129  value(std::forward<ValueT>(value)) {}
130 
131  /// Get a uniqued instance of this lattice anchor class with the given
132  /// arguments.
133  template <typename... Args>
134  static ConcreteT *get(StorageUniquer &uniquer, Args &&...args) {
135  return uniquer.get<ConcreteT>(/*initFn=*/{}, std::forward<Args>(args)...);
136  }
137 
138  /// Allocate space for a lattice anchor and construct it in-place.
139  template <typename ValueT>
140  static ConcreteT *construct(StorageUniquer::StorageAllocator &alloc,
141  ValueT &&value) {
142  return new (alloc.allocate<ConcreteT>())
143  ConcreteT(std::forward<ValueT>(value));
144  }
145 
146  /// Two lattice anchors are equal if their values are equal.
147  bool operator==(const Value &value) const { return this->value == value; }
148 
149  /// Provide LLVM-style RTTI using type IDs.
150  static bool classof(const GenericLatticeAnchor *point) {
151  return point->getTypeID() == TypeID::get<ConcreteT>();
152  }
153 
154  /// Get the contents of the lattice anchor.
155  const Value &getValue() const { return value; }
156 
157 private:
158  /// The lattice anchor value.
159  Value value;
160 };
161 
162 //===----------------------------------------------------------------------===//
163 // LatticeAnchor
164 //===----------------------------------------------------------------------===//
165 
166 /// Fundamental IR components are supported as first-class lattice anchor.
168  : public PointerUnion<GenericLatticeAnchor *, ProgramPoint, Value> {
170  /// Inherit constructors.
171  using ParentTy::PointerUnion;
172  /// Allow implicit conversion from the parent type.
173  LatticeAnchor(ParentTy point = nullptr) : ParentTy(point) {}
174  /// Allow implicit conversions from operation wrappers.
175  /// TODO: For Windows only. Find a better solution.
176  template <typename OpT, typename = std::enable_if_t<
177  std::is_convertible<OpT, Operation *>::value &&
178  !std::is_same<OpT, Operation *>::value>>
180 
183 
184  /// Print the lattice anchor.
185  void print(raw_ostream &os) const;
186 
187  /// Get the source location of the lattice anchor.
188  Location getLoc() const;
189 };
190 
191 /// Forward declaration of the data-flow analysis class.
192 class DataFlowAnalysis;
193 
194 //===----------------------------------------------------------------------===//
195 // DataFlowConfig
196 //===----------------------------------------------------------------------===//
197 
198 /// Configuration class for data flow solver and child analyses. Follows the
199 /// fluent API pattern.
201 public:
202  DataFlowConfig() = default;
203 
204  /// Set whether the solver should operate interpocedurally, i.e. enter the
205  /// callee body when available. Interprocedural analyses may be more precise,
206  /// but also more expensive as more states need to be computed and the
207  /// fixpoint convergence takes longer.
209  interprocedural = enable;
210  return *this;
211  }
212 
213  /// Return `true` if the solver operates interprocedurally, `false` otherwise.
214  bool isInterprocedural() const { return interprocedural; }
215 
216 private:
217  bool interprocedural = true;
218 };
219 
220 //===----------------------------------------------------------------------===//
221 // DataFlowSolver
222 //===----------------------------------------------------------------------===//
223 
224 /// The general data-flow analysis solver. This class is responsible for
225 /// orchestrating child data-flow analyses, running the fixed-point iteration
226 /// algorithm, managing analysis state and lattice anchor memory, and tracking
227 /// dependencies between analyses, lattice anchor, and analysis states.
228 ///
229 /// Steps to run a data-flow analysis:
230 ///
231 /// 1. Load and initialize children analyses. Children analyses are instantiated
232 /// in the solver and initialized, building their dependency relations.
233 /// 2. Configure and run the analysis. The solver invokes the children analyses
234 /// according to their dependency relations until a fixed point is reached.
235 /// 3. Query analysis state results from the solver.
236 ///
237 /// TODO: Optimize the internal implementation of the solver.
239 public:
240  explicit DataFlowSolver(const DataFlowConfig &config = DataFlowConfig())
241  : config(config) {}
242 
243  /// Load an analysis into the solver. Return the analysis instance.
244  template <typename AnalysisT, typename... Args>
245  AnalysisT *load(Args &&...args);
246 
247  /// Initialize the children analyses starting from the provided top-level
248  /// operation and run the analysis until fixpoint.
249  LogicalResult initializeAndRun(Operation *top);
250 
251  /// Lookup an analysis state for the given lattice anchor. Returns null if one
252  /// does not exist.
253  template <typename StateT, typename AnchorT>
254  const StateT *lookupState(AnchorT anchor) const {
255  auto it =
256  analysisStates.find({LatticeAnchor(anchor), TypeID::get<StateT>()});
257  if (it == analysisStates.end())
258  return nullptr;
259  return static_cast<const StateT *>(it->second.get());
260  }
261 
262  /// Erase any analysis state associated with the given lattice anchor.
263  template <typename AnchorT>
264  void eraseState(AnchorT anchor) {
265  LatticeAnchor la(anchor);
266 
267  for (auto it = analysisStates.begin(); it != analysisStates.end(); ++it) {
268  if (it->first.first == la)
269  analysisStates.erase(it);
270  }
271  }
272 
273  /// Get a uniqued lattice anchor instance. If one is not present, it is
274  /// created with the provided arguments.
275  template <typename AnchorT, typename... Args>
276  AnchorT *getLatticeAnchor(Args &&...args) {
277  return AnchorT::get(uniquer, std::forward<Args>(args)...);
278  }
279 
280  /// A work item on the solver queue is a program point, child analysis pair.
281  /// Each item is processed by invoking the child analysis at the program
282  /// point.
283  using WorkItem = std::pair<ProgramPoint, DataFlowAnalysis *>;
284  /// Push a work item onto the worklist.
285  void enqueue(WorkItem item) { worklist.push(std::move(item)); }
286 
287  /// Get the state associated with the given lattice anchor. If it does not
288  /// exist, create an uninitialized state.
289  template <typename StateT, typename AnchorT>
290  StateT *getOrCreateState(AnchorT anchor);
291 
292  /// Propagate an update to an analysis state if it changed by pushing
293  /// dependent work items to the back of the queue.
294  void propagateIfChanged(AnalysisState *state, ChangeResult changed);
295 
296  /// Get the configuration of the solver.
297  const DataFlowConfig &getConfig() const { return config; }
298 
299 private:
300  /// Configuration of the dataflow solver.
301  DataFlowConfig config;
302 
303  /// The solver's work queue. Work items can be inserted to the front of the
304  /// queue to be processed greedily, speeding up computations that otherwise
305  /// quickly degenerate to quadratic due to propagation of state updates.
306  std::queue<WorkItem> worklist;
307 
308  /// Type-erased instances of the children analyses.
310 
311  /// The storage uniquer instance that owns the memory of the allocated lattice
312  /// anchors
313  StorageUniquer uniquer;
314 
315  /// A type-erased map of lattice anchors to associated analysis states for
316  /// first-class lattice anchors.
317  DenseMap<std::pair<LatticeAnchor, TypeID>, std::unique_ptr<AnalysisState>>
318  analysisStates;
319 
320  /// Allow the base child analysis class to access the internals of the solver.
321  friend class DataFlowAnalysis;
322 };
323 
324 //===----------------------------------------------------------------------===//
325 // AnalysisState
326 //===----------------------------------------------------------------------===//
327 
328 /// Base class for generic analysis states. Analysis states contain data-flow
329 /// information that are attached to lattice anchors and which evolve as the
330 /// analysis iterates.
331 ///
332 /// This class places no restrictions on the semantics of analysis states beyond
333 /// these requirements.
334 ///
335 /// 1. Querying the state of a lattice anchor prior to visiting that anchor
336 /// results in uninitialized state. Analyses must be aware of unintialized
337 /// states.
338 /// 2. Analysis states can reach fixpoints, where subsequent updates will never
339 /// trigger a change in the state.
340 /// 3. Analysis states that are uninitialized can be forcefully initialized to a
341 /// default value.
343 public:
344  virtual ~AnalysisState();
345 
346  /// Create the analysis state at the given lattice anchor.
348 
349  /// Returns the lattice anchor this state is located at.
350  LatticeAnchor getAnchor() const { return anchor; }
351 
352  /// Print the contents of the analysis state.
353  virtual void print(raw_ostream &os) const = 0;
354  LLVM_DUMP_METHOD void dump() const;
355 
356  /// Add a dependency to this analysis state on a lattice anchor and an
357  /// analysis. If this state is updated, the analysis will be invoked on the
358  /// given lattice anchor again (in onUpdate()).
359  void addDependency(ProgramPoint point, DataFlowAnalysis *analysis);
360 
361 protected:
362  /// This function is called by the solver when the analysis state is updated
363  /// to enqueue more work items. For example, if a state tracks dependents
364  /// through the IR (e.g. use-def chains), this function can be implemented to
365  /// push those dependents on the worklist.
366  virtual void onUpdate(DataFlowSolver *solver) const {
367  for (const DataFlowSolver::WorkItem &item : dependents)
368  solver->enqueue(item);
369  }
370 
371  /// The lattice anchor to which the state belongs.
373 
374 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
375  /// When compiling with debugging, keep a name for the analysis state.
376  StringRef debugName;
377 #endif // LLVM_ENABLE_ABI_BREAKING_CHECKS
378 
379 private:
380  /// The dependency relations originating from this analysis state. An entry
381  /// `state -> (analysis, anchor)` is created when `analysis` queries `state`
382  /// when updating `anchor`.
383  ///
384  /// When this state is updated, all dependent child analysis invocations are
385  /// pushed to the back of the queue. Use a `SetVector` to keep the analysis
386  /// deterministic.
387  ///
388  /// Store the dependents on the analysis state for efficiency.
390 
391  /// Allow the framework to access the dependents.
392  friend class DataFlowSolver;
393 };
394 
395 //===----------------------------------------------------------------------===//
396 // DataFlowAnalysis
397 //===----------------------------------------------------------------------===//
398 
399 /// Base class for all data-flow analyses. A child analysis is expected to build
400 /// an initial dependency graph (and optionally provide an initial state) when
401 /// initialized and define transfer functions when visiting program points.
402 ///
403 /// In classical data-flow analysis, the dependency graph is fixed and analyses
404 /// define explicit transfer functions between input states and output states.
405 /// In this framework, however, the dependency graph can change during the
406 /// analysis, and transfer functions are opaque such that the solver doesn't
407 /// know what states calling `visit` on an analysis will be updated. This allows
408 /// multiple analyses to plug in and provide values for the same state.
409 ///
410 /// Generally, when an analysis queries an uninitialized state, it is expected
411 /// to "bail out", i.e., not provide any updates. When the value is initialized,
412 /// the solver will re-invoke the analysis. If the solver exhausts its worklist,
413 /// however, and there are still uninitialized states, the solver "nudges" the
414 /// analyses by default-initializing those states.
416 public:
417  virtual ~DataFlowAnalysis();
418 
419  /// Create an analysis with a reference to the parent solver.
420  explicit DataFlowAnalysis(DataFlowSolver &solver);
421 
422  /// Initialize the analysis from the provided top-level operation by building
423  /// an initial dependency graph between all lattice anchors of interest. This
424  /// can be implemented by calling `visit` on all program points of interest
425  /// below the top-level operation.
426  ///
427  /// An analysis can optionally provide initial values to certain analysis
428  /// states to influence the evolution of the analysis.
429  virtual LogicalResult initialize(Operation *top) = 0;
430 
431  /// Visit the given program point. This function is invoked by the solver on
432  /// this analysis with a given program point when a dependent analysis state
433  /// is updated. The function is similar to a transfer function; it queries
434  /// certain analysis states and sets other states.
435  ///
436  /// The function is expected to create dependencies on queried states and
437  /// propagate updates on changed states. A dependency can be created by
438  /// calling `addDependency` between the input state and a program point,
439  /// indicating that, if the state is updated, the solver should invoke `solve`
440  /// on the program point. The dependent point does not have to be the same as
441  /// the provided point. An update to a state is propagated by calling
442  /// `propagateIfChange` on the state. If the state has changed, then all its
443  /// dependents are placed on the worklist.
444  ///
445  /// The dependency graph does not need to be static. Each invocation of
446  /// `visit` can add new dependencies, but these dependencies will not be
447  /// dynamically added to the worklist because the solver doesn't know what
448  /// will provide a value for then.
449  virtual LogicalResult visit(ProgramPoint point) = 0;
450 
451 protected:
452  /// Create a dependency between the given analysis state and lattice anchor
453  /// on this analysis.
454  void addDependency(AnalysisState *state, ProgramPoint point);
455 
456  /// Propagate an update to a state if it changed.
457  void propagateIfChanged(AnalysisState *state, ChangeResult changed);
458 
459  /// Register a custom lattice anchor class.
460  template <typename AnchorT>
462  solver.uniquer.registerParametricStorageType<AnchorT>();
463  }
464 
465  /// Get or create a custom lattice anchor.
466  template <typename AnchorT, typename... Args>
467  AnchorT *getLatticeAnchor(Args &&...args) {
468  return solver.getLatticeAnchor<AnchorT>(std::forward<Args>(args)...);
469  }
470 
471  /// Get the analysis state associated with the lattice anchor. The returned
472  /// state is expected to be "write-only", and any updates need to be
473  /// propagated by `propagateIfChanged`.
474  template <typename StateT, typename AnchorT>
475  StateT *getOrCreate(AnchorT anchor) {
476  return solver.getOrCreateState<StateT>(anchor);
477  }
478 
479  /// Get a read-only analysis state for the given point and create a dependency
480  /// on `dependent`. If the return state is updated elsewhere, this analysis is
481  /// re-invoked on the dependent.
482  template <typename StateT, typename AnchorT>
483  const StateT *getOrCreateFor(ProgramPoint dependent, AnchorT anchor) {
484  StateT *state = getOrCreate<StateT>(anchor);
485  addDependency(state, dependent);
486  return state;
487  }
488 
489  /// Return the configuration of the solver used for this analysis.
490  const DataFlowConfig &getSolverConfig() const { return solver.getConfig(); }
491 
492 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
493  /// When compiling with debugging, keep a name for the analyis.
494  StringRef debugName;
495 #endif // LLVM_ENABLE_ABI_BREAKING_CHECKS
496 
497 private:
498  /// The parent data-flow solver.
499  DataFlowSolver &solver;
500 
501  /// Allow the data-flow solver to access the internals of this class.
502  friend class DataFlowSolver;
503 };
504 
505 template <typename AnalysisT, typename... Args>
506 AnalysisT *DataFlowSolver::load(Args &&...args) {
507  childAnalyses.emplace_back(new AnalysisT(*this, std::forward<Args>(args)...));
508 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
509  childAnalyses.back().get()->debugName = llvm::getTypeName<AnalysisT>();
510 #endif // LLVM_ENABLE_ABI_BREAKING_CHECKS
511  return static_cast<AnalysisT *>(childAnalyses.back().get());
512 }
513 
514 template <typename StateT, typename AnchorT>
515 StateT *DataFlowSolver::getOrCreateState(AnchorT anchor) {
516  std::unique_ptr<AnalysisState> &state =
517  analysisStates[{LatticeAnchor(anchor), TypeID::get<StateT>()}];
518  if (!state) {
519  state = std::unique_ptr<StateT>(new StateT(anchor));
520 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
521  state->debugName = llvm::getTypeName<StateT>();
522 #endif // LLVM_ENABLE_ABI_BREAKING_CHECKS
523  }
524  return static_cast<StateT *>(state.get());
525 }
526 
527 inline raw_ostream &operator<<(raw_ostream &os, const AnalysisState &state) {
528  state.print(os);
529  return os;
530 }
531 
532 inline raw_ostream &operator<<(raw_ostream &os, LatticeAnchor anchor) {
533  anchor.print(os);
534  return os;
535 }
536 
537 } // end namespace mlir
538 
539 namespace llvm {
540 /// Allow hashing of lattice anchors and program points.
541 template <>
542 struct DenseMapInfo<mlir::LatticeAnchor>
543  : public DenseMapInfo<mlir::LatticeAnchor::ParentTy> {};
544 
545 template <>
546 struct DenseMapInfo<mlir::ProgramPoint>
547  : public DenseMapInfo<mlir::ProgramPoint::ParentTy> {};
548 
549 // Allow llvm::cast style functions.
550 template <typename To>
551 struct CastInfo<To, mlir::LatticeAnchor>
552  : public CastInfo<To, mlir::LatticeAnchor::PointerUnion> {};
553 
554 template <typename To>
555 struct CastInfo<To, const mlir::LatticeAnchor>
556  : public CastInfo<To, const mlir::LatticeAnchor::PointerUnion> {};
557 
558 template <typename To>
559 struct CastInfo<To, mlir::ProgramPoint>
560  : public CastInfo<To, mlir::ProgramPoint::PointerUnion> {};
561 
562 template <typename To>
563 struct CastInfo<To, const mlir::ProgramPoint>
564  : public CastInfo<To, const mlir::ProgramPoint::PointerUnion> {};
565 
566 /// Allow stealing the low bits of a ProgramPoint.
567 template <>
568 struct PointerLikeTypeTraits<mlir::ProgramPoint>
569  : public PointerLikeTypeTraits<mlir::ProgramPoint::ParentTy> {};
570 
571 } // end namespace llvm
572 
573 #endif // MLIR_ANALYSIS_DATAFLOWFRAMEWORK_H
Base class for generic analysis states.
AnalysisState(LatticeAnchor anchor)
Create the analysis state at the given lattice anchor.
LLVM_DUMP_METHOD void dump() const
LatticeAnchor getAnchor() const
Returns the lattice anchor this state is located at.
void addDependency(ProgramPoint point, DataFlowAnalysis *analysis)
Add a dependency to this analysis state on a lattice anchor and an analysis.
virtual void print(raw_ostream &os) const =0
Print the contents of the analysis state.
virtual void onUpdate(DataFlowSolver *solver) const
This function is called by the solver when the analysis state is updated to enqueue more work items.
virtual ~AnalysisState()
LatticeAnchor anchor
The lattice anchor to which the state belongs.
Block represents an ordered list of Operations.
Definition: Block.h:31
Base class for all data-flow analyses.
void propagateIfChanged(AnalysisState *state, ChangeResult changed)
Propagate an update to a state if it changed.
virtual LogicalResult visit(ProgramPoint point)=0
Visit the given program point.
AnchorT * getLatticeAnchor(Args &&...args)
Get or create a custom lattice anchor.
StateT * getOrCreate(AnchorT anchor)
Get the analysis state associated with the lattice anchor.
const StateT * getOrCreateFor(ProgramPoint dependent, AnchorT anchor)
Get a read-only analysis state for the given point and create a dependency on dependent.
const DataFlowConfig & getSolverConfig() const
Return the configuration of the solver used for this analysis.
DataFlowAnalysis(DataFlowSolver &solver)
Create an analysis with a reference to the parent solver.
virtual LogicalResult initialize(Operation *top)=0
Initialize the analysis from the provided top-level operation by building an initial dependency graph...
void addDependency(AnalysisState *state, ProgramPoint point)
Create a dependency between the given analysis state and lattice anchor on this analysis.
void registerAnchorKind()
Register a custom lattice anchor class.
Configuration class for data flow solver and child analyses.
DataFlowConfig()=default
DataFlowConfig & setInterprocedural(bool enable)
Set whether the solver should operate interpocedurally, i.e.
bool isInterprocedural() const
Return true if the solver operates interprocedurally, false otherwise.
The general data-flow analysis solver.
void enqueue(WorkItem item)
Push a work item onto the worklist.
void eraseState(AnchorT anchor)
Erase any analysis state associated with the given lattice anchor.
void propagateIfChanged(AnalysisState *state, ChangeResult changed)
Propagate an update to an analysis state if it changed by pushing dependent work items to the back of...
const StateT * lookupState(AnchorT anchor) const
Lookup an analysis state for the given lattice anchor.
const DataFlowConfig & getConfig() const
Get the configuration of the solver.
AnchorT * getLatticeAnchor(Args &&...args)
Get a uniqued lattice anchor instance.
StateT * getOrCreateState(AnchorT anchor)
Get the state associated with the given lattice anchor.
AnalysisT * load(Args &&...args)
Load an analysis into the solver. Return the analysis instance.
LogicalResult initializeAndRun(Operation *top)
Initialize the children analyses starting from the provided top-level operation and run the analysis ...
DataFlowSolver(const DataFlowConfig &config=DataFlowConfig())
std::pair< ProgramPoint, DataFlowAnalysis * > WorkItem
A work item on the solver queue is a program point, child analysis pair.
Base class for generic lattice anchor based on a concrete lattice anchor type and a content key.
bool operator==(const Value &value) const
Two lattice anchors are equal if their values are equal.
static ConcreteT * construct(StorageUniquer::StorageAllocator &alloc, ValueT &&value)
Allocate space for a lattice anchor and construct it in-place.
static bool classof(const GenericLatticeAnchor *point)
Provide LLVM-style RTTI using type IDs.
GenericLatticeAnchorBase(ValueT &&value)
Construct an instance of the lattice anchor using the provided value and the type ID of the concrete ...
const Value & getValue() const
Get the contents of the lattice anchor.
static ConcreteT * get(StorageUniquer &uniquer, Args &&...args)
Get a uniqued instance of this lattice anchor class with the given arguments.
Abstract class for generic lattice anchor.
virtual void print(raw_ostream &os) const =0
Print the lattice anchor.
TypeID getTypeID() const
Get the abstract lattice anchor's type identifier.
virtual Location getLoc() const =0
Get a derived source location for the lattice anchor.
GenericLatticeAnchor(TypeID typeID)
Create an abstract lattice anchor with type identifier.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:63
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
This class acts as the base storage that all storage classes must derived from.
This is a utility allocator used to allocate memory for instances of derived types.
T * allocate()
Allocate an instance of the provided type.
A utility class to get or create instances of "storage classes".
Storage * get(function_ref< void(Storage *)> initFn, TypeID id, Args &&...args)
Gets a uniqued instance of 'Storage'.
void registerParametricStorageType(TypeID id)
Register a new parametric storage class, this is necessary to create instances of this class type.
This class provides an efficient unique identifier for a specific C++ type.
Definition: TypeID.h:104
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:96
The OpAsmOpInterface, see OpAsmInterface.td for more details.
Definition: CallGraph.h:229
Include the generated interface declarations.
ChangeResult & operator|=(ChangeResult &lhs, ChangeResult rhs)
ChangeResult
A result type used to indicate if a change happened.
ChangeResult operator&(ChangeResult lhs, ChangeResult rhs)
ChangeResult operator|(ChangeResult lhs, ChangeResult rhs)
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
raw_ostream & operator<<(raw_ostream &os, const AliasResult &result)
Definition: AliasAnalysis.h:78
Fundamental IR components are supported as first-class lattice anchor.
LatticeAnchor(Block *block)
LatticeAnchor(ParentTy point=nullptr)
Allow implicit conversion from the parent type.
LatticeAnchor(Operation *op)
Location getLoc() const
Get the source location of the lattice anchor.
LatticeAnchor(OpT op)
Allow implicit conversions from operation wrappers.
void print(raw_ostream &os) const
Print the lattice anchor.
Program point represents a specific location in the execution of a program.
ProgramPoint(ParentTy point=nullptr)
Allow implicit conversion from the parent type.
ProgramPoint(OpT op)
Allow implicit conversions from operation wrappers.
void print(raw_ostream &os) const
Print the program point.