Enzyme main
Loading...
Searching...
No Matches
DataFlowAliasAnalysis.h
Go to the documentation of this file.
1//===- DataflowAliasAnalysis.h - Declaration of Alias Analysis ------------===//
2//
3// Enzyme Project
4//
5// Part of the Enzyme Project, under the Apache License v2.0 with LLVM
6// Exceptions. See https://llvm.org/LICENSE.txt for license information.
7// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8//
9// If using this code in an academic setting, please cite the following:
10// @incollection{enzymeNeurips,
11// title = {Instead of Rewriting Foreign Code for Machine Learning,
12// Automatically Synthesize Fast Gradients},
13// author = {Moses, William S. and Churavy, Valentin},
14// booktitle = {Advances in Neural Information Processing Systems 33},
15// year = {2020},
16// note = {To appear in},
17// }
18//
19//===----------------------------------------------------------------------===//
20//
21// This file contains the declaration of Alias (and Points-To) Analysis, a
22// general analysis that determines the possible static memory locations
23// that the pointers in a program may point to.
24//
25//===----------------------------------------------------------------------===//
26#ifndef ENZYME_MLIR_ANALYSIS_DATAFLOW_ALIASANALYSIS_H
27#define ENZYME_MLIR_ANALYSIS_DATAFLOW_ALIASANALYSIS_H
28
29#include "DataFlowLattice.h"
30
31#include "mlir/Analysis/AliasAnalysis.h"
32#include "mlir/Analysis/DataFlow/DenseAnalysis.h"
33#include "mlir/Analysis/DataFlow/SparseAnalysis.h"
34#include "mlir/Analysis/DataFlowFramework.h"
35#include "mlir/Interfaces/SideEffectInterfaces.h"
36
37namespace mlir {
38
39class CallableOpInterface;
40
41namespace enzyme {
42
43/// A set of alias class identifiers to be treated as a single union. May be
44/// marked as "unknown", which is a conservative pessimistic state, or as
45/// "undefined", which is a "not-yet-analyzed" initial state. Undefined state is
46/// different from an empty alias set.
48
49//===----------------------------------------------------------------------===//
50// OriginalClasses
51//===----------------------------------------------------------------------===//
52
53/// Alias classes for freshly created, e.g., allocated values. These must
54/// be used instead of allocating a fresh distinct attribute every time.
55/// Allocation may only happen when the mapping is not already present here.
57public:
58 DistinctAttr getOriginalClass(Value value, StringRef debugLabel) {
59 return getOriginalClass(value,
60 StringAttr::get(value.getContext(), debugLabel));
61 }
62 DistinctAttr getOriginalClass(Value value, Attribute referenced = nullptr) {
63 DistinctAttr &aliasClass = originalClasses[value];
64 if (!aliasClass) {
65 if (!referenced)
66 referenced = UnitAttr::get(value.getContext());
67 aliasClass = DistinctAttr::create(referenced);
68 }
69 return aliasClass;
70 }
71
72 DistinctAttr getSameOriginalClass(ValueRange values, StringRef debugLabel) {
73 if (values.empty())
74 return nullptr;
75
76 auto label = StringAttr::get(values.front().getContext(), debugLabel);
77
78 DistinctAttr common = nullptr;
79 for (Value v : values) {
80 DistinctAttr &aliasClass = originalClasses[v];
81 if (!aliasClass) {
82 if (!common)
83 common = DistinctAttr::create(label);
84 aliasClass = common;
85 } else {
86 if (!common)
87 common = aliasClass;
88 else
89 assert(aliasClass == common && "original alias class mismatch");
90 }
91 }
92 return common;
93 }
94
95private:
96 DenseMap<Value, DistinctAttr> originalClasses;
97};
98
99//===----------------------------------------------------------------------===//
100// PointsToSets
101//
102// Specifically for pointers to pointers. This tracks alias information through
103// pointers stored/loaded through memory.
104//===----------------------------------------------------------------------===//
105
106class PointsToSets : public MapOfSetsLattice<DistinctAttr, DistinctAttr> {
107public:
108 using MapOfSetsLattice::MapOfSetsLattice;
109
110 void print(raw_ostream &os) const override;
111
112 /// Mark the pointer stored in `dest` as possibly pointing to any of `values`,
113 /// instead of the values it may be currently pointing to.
114 ChangeResult setPointingToClasses(const AliasClassSet &destClasses,
115 const AliasClassSet &values) {
116 return update(destClasses, values, /*replace=*/true);
117 }
118
119 /// Mark the pointer stored in `dest` as possibly pointing to any of `values`,
120 /// in addition to the values it may already point to.
121 ChangeResult insert(const AliasClassSet &destClasses,
122 const AliasClassSet &values) {
123 return update(destClasses, values, /*replace=*/false);
124 };
125
126 /// For every alias class in `dest`, record that it may additionally be
127 /// pointing to the same as the classes in `src`.
128 ChangeResult addSetsFrom(const AliasClassSet &destClasses,
129 const AliasClassSet &srcClasses);
130
131 ChangeResult setPointingToEmpty(const AliasClassSet &destClasses);
132
133 /// Mark `dest` as pointing to "unknown" alias set, that is, any possible
134 /// other pointer. This is partial pessimistic fixpoint.
135 ChangeResult markPointToUnknown(const AliasClassSet &destClasses);
136
137 /// Mark the entire data structure as "unknown", that is, any pointer may be
138 /// containing any other pointer. This is the full pessimistic fixpoint.
139 ChangeResult markAllPointToUnknown() { return markAllUnknown(); }
140
141 /// Mark all alias classes except the given ones to point to the "unknown"
142 /// alias set.
143 ChangeResult markAllExceptPointToUnknown(const AliasClassSet &destClasses);
144
145 const AliasClassSet &getPointsTo(DistinctAttr id) const { return lookup(id); }
146
147private:
148 /// Update all alias classes in `keysToUpdate` to additionally point to alias
149 /// classes in `values`. Handle undefined keys optimistically (ignore) and
150 /// unknown keys pessimistically (update all existing keys). `replace` is a
151 /// debugging aid that indicates whether the update is intended to replace the
152 /// pre-existing state, it has no effect in NoAsserts build. Since we don't
153 /// want to forcefully reset pointsTo value as that is not guaranteed to make
154 /// monotonous progress on the lattice and therefore convergence to fixpoint,
155 /// replacement is only expected for a previously "unknown" value (absent from
156 /// the mapping) or for a value with itself. Replacement is therefore handled
157 /// as a regular update, i.e. join, with additional assertions. Note that
158 /// currently an update is possible to _any_ value that is >= the current one
159 /// in the lattice, not only the replacements described above.
160 ChangeResult update(const AliasClassSet &keysToUpdate,
161 const AliasClassSet &values, bool replace);
162};
163
164//===----------------------------------------------------------------------===//
165// PointsToPointerAnalysis
166//===----------------------------------------------------------------------===//
167
169 : public dataflow::DenseForwardDataFlowAnalysis<PointsToSets> {
170public:
171 PointsToPointerAnalysis(DataFlowSolver &solver)
173
174 void setToEntryState(PointsToSets *lattice) override;
175
176 LogicalResult visitOperation(Operation *op, const PointsToSets &before,
177 PointsToSets *after) override;
178
179 void visitCallControlFlowTransfer(CallOpInterface call,
180 dataflow::CallControlFlowAction action,
181 const PointsToSets &before,
182 PointsToSets *after) override;
183
184 void processCapturingStore(ProgramPoint *dependent, PointsToSets *after,
185 Value capturedValue, Value destinationAddress,
186 bool isMustStore = false);
187
189 CallOpInterface call,
190 const DenseMap<DistinctAttr, AliasClassSet> &summary,
191 PointsToSets *after);
192
193private:
194 /// Alias classes originally assigned to known-distinct values, e.g., fresh
195 /// allocations, by this analysis. This does NOT necessarily need to be shared
196 /// with the other analysis as they may assign different classes, e.g., for
197 /// results of the same call.
198 OriginalClasses originalClasses;
199};
200
201//===----------------------------------------------------------------------===//
202// AliasClassLattice
203//===----------------------------------------------------------------------===//
204
205class AliasClassLattice : public SparseSetLattice<DistinctAttr> {
206public:
208
209 void print(raw_ostream &os) const override;
210
211 ::mlir::AliasResult alias(const AbstractSparseLattice &other) const;
212
213 ChangeResult join(const AbstractSparseLattice &other) override;
214
215 static AliasClassLattice single(Value point, DistinctAttr value) {
216 return AliasClassLattice(point, AliasClassSet(value));
217 }
218
219 const DenseSet<DistinctAttr> &getAliasClasses() const {
220 return elements.getElements();
221 }
222
224};
225
226//===----------------------------------------------------------------------===//
227// AliasAnalysis
228//===----------------------------------------------------------------------===//
229
230/// This analysis implements interprocedural alias analysis
232 : public dataflow::SparseForwardDataFlowAnalysis<AliasClassLattice> {
233public:
234 AliasAnalysis(DataFlowSolver &solver, MLIRContext *ctx, bool relative = false)
236 entryClass(DistinctAttr::create(StringAttr::get(ctx, "entry"))),
237 relative(relative) {
238 if (relative)
239 assert(!solver.getConfig().isInterprocedural());
240 }
241
242 void setToEntryState(AliasClassLattice *lattice) override;
243
244 LogicalResult visitOperation(Operation *op,
245 ArrayRef<const AliasClassLattice *> operands,
246 ArrayRef<AliasClassLattice *> results) override;
247
248 void visitExternalCall(CallOpInterface call,
249 ArrayRef<const AliasClassLattice *> operands,
250 ArrayRef<AliasClassLattice *> results) override;
251
252private:
253 void transfer(Operation *op, ArrayRef<MemoryEffects::EffectInstance> effects,
254 ArrayRef<const AliasClassLattice *> operands,
255 ArrayRef<AliasClassLattice *> results);
256
257 /// Create a pseudo alias class when loading from a function argument that
258 /// points to unknown locations. The pseudo class indicates that it points to
259 /// _something_ and is expected to be unified with a concrete alias class when
260 /// the function summaries are used at this function's call sites.
261 void createImplicitArgDereference(Operation *op, AliasClassLattice *source,
262 DistinctAttr srcClass,
263 AliasClassLattice *result);
264
265 /// A special alias class to denote unannotated pointer arguments.
266 const DistinctAttr entryClass;
267
268 /// If true, the analysis will operate in an intraprocedural way assuming it
269 /// is called bottom-up on the function call graph.
270 const bool relative;
271
272 /// Alias classes originally assigned to known-distinct values, e.g., fresh
273 /// allocations, by this analysis. This does NOT necessarily need to be shared
274 /// with the other analysis as they may assign different classes, e.g., for
275 /// results of the same call.
276 OriginalClasses originalClasses;
277};
278
279} // namespace enzyme
280} // namespace mlir
281
282#endif
static bool isMustStore(Operation *op, Value pointer)
This analysis implements interprocedural alias analysis.
LogicalResult visitOperation(Operation *op, ArrayRef< const AliasClassLattice * > operands, ArrayRef< AliasClassLattice * > results) override
void visitExternalCall(CallOpInterface call, ArrayRef< const AliasClassLattice * > operands, ArrayRef< AliasClassLattice * > results) override
AliasAnalysis(DataFlowSolver &solver, MLIRContext *ctx, bool relative=false)
void setToEntryState(AliasClassLattice *lattice) override
const AliasClassSet & getAliasClassesObject() const
void print(raw_ostream &os) const override
const DenseSet< DistinctAttr > & getAliasClasses() const
ChangeResult join(const AbstractSparseLattice &other) override
static AliasClassLattice single(Value point, DistinctAttr value)
::mlir::AliasResult alias(const AbstractSparseLattice &other) const
const SetLattice< DistinctAttr > & lookup(DistinctAttr key) const
Alias classes for freshly created, e.g., allocated values.
DistinctAttr getSameOriginalClass(ValueRange values, StringRef debugLabel)
DistinctAttr getOriginalClass(Value value, Attribute referenced=nullptr)
DistinctAttr getOriginalClass(Value value, StringRef debugLabel)
void processCallToSummarizedFunc(CallOpInterface call, const DenseMap< DistinctAttr, AliasClassSet > &summary, PointsToSets *after)
void setToEntryState(PointsToSets *lattice) override
void processCapturingStore(ProgramPoint *dependent, PointsToSets *after, Value capturedValue, Value destinationAddress, bool isMustStore=false)
LogicalResult visitOperation(Operation *op, const PointsToSets &before, PointsToSets *after) override
void visitCallControlFlowTransfer(CallOpInterface call, dataflow::CallControlFlowAction action, const PointsToSets &before, PointsToSets *after) override
ChangeResult insert(const AliasClassSet &destClasses, const AliasClassSet &values)
Mark the pointer stored in dest as possibly pointing to any of values, in addition to the values it m...
const AliasClassSet & getPointsTo(DistinctAttr id) const
ChangeResult markAllExceptPointToUnknown(const AliasClassSet &destClasses)
Mark all alias classes except the given ones to point to the "unknown" alias set.
ChangeResult setPointingToClasses(const AliasClassSet &destClasses, const AliasClassSet &values)
Mark the pointer stored in dest as possibly pointing to any of values, instead of the values it may b...
ChangeResult markAllPointToUnknown()
Mark the entire data structure as "unknown", that is, any pointer may be containing any other pointer...
ChangeResult markPointToUnknown(const AliasClassSet &destClasses)
Mark dest as pointing to "unknown" alias set, that is, any possible other pointer.
ChangeResult setPointingToEmpty(const AliasClassSet &destClasses)
void print(raw_ostream &os) const override
ChangeResult addSetsFrom(const AliasClassSet &destClasses, const AliasClassSet &srcClasses)
For every alias class in dest, record that it may additionally be pointing to the same as the classes...
DenseSet< ValueT > & getElements()
SparseSetLattice(Value value, SetLattice< ValueT > &&elements)
SetLattice< DistinctAttr > AliasClassSet
A set of alias class identifiers to be treated as a single union.