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