xref: /llvm-project/mlir/include/mlir/Analysis/Presburger/PresburgerSpace.h (revision 6c7a3f80e75de36f2642110a077664e948d9e7e3)
1 //===- PresburgerSpace.h - MLIR PresburgerSpace Class -----------*- 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 // Classes representing space information like number of variables and kind of
10 // variables.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef MLIR_ANALYSIS_PRESBURGER_PRESBURGERSPACE_H
15 #define MLIR_ANALYSIS_PRESBURGER_PRESBURGERSPACE_H
16 
17 #include "llvm/ADT/ArrayRef.h"
18 #include "llvm/ADT/SmallVector.h"
19 #include "llvm/Support/PointerLikeTypeTraits.h"
20 #include "llvm/Support/TypeName.h"
21 #include "llvm/Support/raw_ostream.h"
22 
23 namespace mlir {
24 namespace presburger {
25 using llvm::ArrayRef;
26 using llvm::SmallVector;
27 
28 /// Kind of variable. Implementation wise SetDims are treated as Range
29 /// vars, and spaces with no distinction between dimension vars are treated
30 /// as relations with zero domain vars.
31 enum class VarKind { Symbol, Local, Domain, Range, SetDim = Range };
32 
33 /// An Identifier stores a pointer to an object, such as a Value or an
34 /// Operation. Identifiers are intended to be attached to a variable in a
35 /// PresburgerSpace and can be used to check if two variables correspond to the
36 /// same object.
37 ///
38 /// Take for example the following code:
39 ///
40 /// for i = 0 to 100
41 ///   for j = 0 to 100
42 ///     S0: A[j] = 0
43 ///   for k = 0 to 100
44 ///     S1: A[k] = 1
45 ///
46 /// If we represent the space of iteration variables surrounding S0, S1 we have:
47 /// space(S0): {d0, d1}
48 /// space(S1): {d0, d1}
49 ///
50 /// Since the variables are in different spaces, without an identifier, there
51 /// is no way to distinguish if the variables in the two spaces correspond to
52 /// different SSA values in the program. So, we attach an Identifier
53 /// corresponding to the loop iteration variable to them. Now,
54 ///
55 /// space(S0) = {d0(id = i), d1(id = j)}
56 /// space(S1) = {d0(id = i), d1(id = k)}.
57 ///
58 /// Using the identifier, we can check that the first iteration variable in
59 /// both the spaces correspond to the same variable in the program, while they
60 /// are different for second iteration variable.
61 ///
62 /// The equality of Identifiers is checked by comparing the stored pointers.
63 /// Checking equality asserts that the type of the equal identifiers is same.
64 /// Identifiers storing null pointers are treated as having no attachment and
65 /// are considered unequal to any other identifier, including other identifiers
66 /// with no attachments.
67 ///
68 /// The type of the pointer stored must have an `llvm::PointerLikeTypeTraits`
69 /// specialization.
70 class Identifier {
71 public:
72   Identifier() = default;
73 
74   // Create an identifier from a pointer.
75   template <typename T>
76   explicit Identifier(T value)
77       : value(llvm::PointerLikeTypeTraits<T>::getAsVoidPointer(value)) {
78 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
79     idType = llvm::getTypeName<T>();
80 #endif
81   }
82 
83   /// Get the value of the identifier casted to type `T`. `T` here should match
84   /// the type of the identifier used to create it.
85   template <typename T>
86   T getValue() const {
87 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
88     assert(llvm::getTypeName<T>() == idType &&
89            "Identifier was initialized with a different type than the one used "
90            "to retrieve it.");
91 #endif
92     return llvm::PointerLikeTypeTraits<T>::getFromVoidPointer(value);
93   }
94 
95   bool hasValue() const { return value != nullptr; }
96 
97   /// Check if the two identifiers are equal. Null identifiers are considered
98   /// not equal. Asserts if two identifiers are equal but their types are not.
99   bool isEqual(const Identifier &other) const;
100 
101   bool operator==(const Identifier &other) const { return isEqual(other); }
102   bool operator!=(const Identifier &other) const { return !isEqual(other); }
103 
104   void print(llvm::raw_ostream &os) const;
105   void dump() const;
106 
107 private:
108   /// The value of the identifier.
109   void *value = nullptr;
110 
111 #if LLVM_ENABLE_ABI_BREAKING_CHECKS
112   /// TypeID of the identifiers in space. This should be used in asserts only.
113   llvm::StringRef idType;
114 #endif
115 };
116 
117 /// PresburgerSpace is the space of all possible values of a tuple of integer
118 /// valued variables/variables. Each variable has one of the three types:
119 ///
120 /// Dimension: Ordinary variables over which the space is represented.
121 ///
122 /// Symbol: Symbol variables correspond to fixed but unknown values.
123 /// Mathematically, a space with symbolic variables is like a
124 /// family of spaces indexed by the symbolic variables.
125 ///
126 /// Local: Local variables correspond to existentially quantified variables.
127 /// For example, consider the space: `(x, exists q)` where x is a dimension
128 /// variable and q is a local variable. Let us put the constraints:
129 ///       `1 <= x <= 7, x = 2q`
130 /// on this space to get the set:
131 ///       `(x) : (exists q : q <= x <= 7, x = 2q)`.
132 /// An assignment to symbolic and dimension variables is valid if there
133 /// exists some assignment to the local variable `q` satisfying these
134 /// constraints. For this example, the set is equivalent to {2, 4, 6}.
135 /// Mathematically, existential quantification can be thought of as the result
136 /// of projection. In this example, `q` is existentially quantified. This can be
137 /// thought of as the result of projecting out `q` from the previous example,
138 /// i.e. we obtained {2, 4, 6} by projecting out the second dimension from
139 /// {(2, 1), (4, 2), (6, 2)}.
140 ///
141 /// Dimension variables are further divided into Domain and Range variables
142 /// to support building relations.
143 ///
144 /// Variables are stored in the following order:
145 ///       [Domain, Range, Symbols, Locals]
146 ///
147 /// A space with no distinction between types of dimension variables can
148 /// be implemented as a space with zero domain. VarKind::SetDim should be used
149 /// to refer to dimensions in such spaces.
150 ///
151 /// Compatibility of two spaces implies that number of variables of each kind
152 /// other than Locals are equal. Equality of two spaces implies that number of
153 /// variables of each kind are equal.
154 ///
155 /// PresburgerSpace optionally also supports attaching an Identifier with each
156 /// non-local variable in the space. This is disabled by default. `resetIds` is
157 /// used to enable/reset these identifiers. The user can identify each variable
158 /// in the space as corresponding to some Identifier. Some example use cases
159 /// are described in the `Identifier` documentation above. The type attached to
160 /// the Identifier can be different for different variables in the space.
161 class PresburgerSpace {
162 public:
163   static PresburgerSpace getRelationSpace(unsigned numDomain = 0,
164                                           unsigned numRange = 0,
165                                           unsigned numSymbols = 0,
166                                           unsigned numLocals = 0) {
167     return PresburgerSpace(numDomain, numRange, numSymbols, numLocals);
168   }
169 
170   static PresburgerSpace getSetSpace(unsigned numDims = 0,
171                                      unsigned numSymbols = 0,
172                                      unsigned numLocals = 0) {
173     return PresburgerSpace(/*numDomain=*/0, /*numRange=*/numDims, numSymbols,
174                            numLocals);
175   }
176 
177   /// Get the domain/range space of this space. The returned space is a set
178   /// space.
179   PresburgerSpace getDomainSpace() const;
180   PresburgerSpace getRangeSpace() const;
181 
182   /// Get the space without local variables.
183   PresburgerSpace getSpaceWithoutLocals() const;
184 
185   unsigned getNumDomainVars() const { return numDomain; }
186   unsigned getNumRangeVars() const { return numRange; }
187   unsigned getNumSetDimVars() const { return numRange; }
188   unsigned getNumSymbolVars() const { return numSymbols; }
189   unsigned getNumLocalVars() const { return numLocals; }
190 
191   unsigned getNumDimVars() const { return numDomain + numRange; }
192   unsigned getNumDimAndSymbolVars() const {
193     return numDomain + numRange + numSymbols;
194   }
195   unsigned getNumVars() const {
196     return numDomain + numRange + numSymbols + numLocals;
197   }
198 
199   /// Get the number of vars of the specified kind.
200   unsigned getNumVarKind(VarKind kind) const;
201 
202   /// Return the index at which the specified kind of var starts.
203   unsigned getVarKindOffset(VarKind kind) const;
204 
205   /// Return the index at Which the specified kind of var ends.
206   unsigned getVarKindEnd(VarKind kind) const;
207 
208   /// Get the number of elements of the specified kind in the range
209   /// [varStart, varLimit).
210   unsigned getVarKindOverlap(VarKind kind, unsigned varStart,
211                              unsigned varLimit) const;
212 
213   /// Return the VarKind of the var at the specified position.
214   VarKind getVarKindAt(unsigned pos) const;
215 
216   /// Insert `num` variables of the specified kind at position `pos`.
217   /// Positions are relative to the kind of variable. Return the absolute
218   /// column position (i.e., not relative to the kind of variable) of the
219   /// first added variable.
220   ///
221   /// If identifiers are being used, the newly added variables have no
222   /// identifiers.
223   unsigned insertVar(VarKind kind, unsigned pos, unsigned num = 1);
224 
225   /// Removes variables of the specified kind in the column range [varStart,
226   /// varLimit). The range is relative to the kind of variable.
227   void removeVarRange(VarKind kind, unsigned varStart, unsigned varLimit);
228 
229   /// Converts variables of the specified kind in the column range [srcPos,
230   /// srcPos + num) to variables of the specified kind at position dstPos. The
231   /// ranges are relative to the kind of variable.
232   ///
233   /// srcKind and dstKind must be different.
234   void convertVarKind(VarKind srcKind, unsigned srcPos, unsigned num,
235                       VarKind dstKind, unsigned dstPos);
236 
237   /// Changes the partition between dimensions and symbols. Depending on the new
238   /// symbol count, either a chunk of dimensional variables immediately before
239   /// the split become symbols, or some of the symbols immediately after the
240   /// split become dimensions.
241   void setVarSymbolSeparation(unsigned newSymbolCount);
242 
243   /// Swaps the posA^th variable of kindA and posB^th variable of kindB.
244   void swapVar(VarKind kindA, VarKind kindB, unsigned posA, unsigned posB);
245 
246   /// Returns true if both the spaces are compatible i.e. if both spaces have
247   /// the same number of variables of each kind (excluding locals).
248   bool isCompatible(const PresburgerSpace &other) const;
249 
250   /// Returns true if both the spaces are equal including local variables i.e.
251   /// if both spaces have the same number of variables of each kind (including
252   /// locals).
253   bool isEqual(const PresburgerSpace &other) const;
254 
255   /// Get the identifier of pos^th variable of the specified kind.
256   Identifier getId(VarKind kind, unsigned pos) const {
257     assert(kind != VarKind::Local && "Local variables have no identifiers");
258     if (!usingIds)
259       return Identifier();
260     return identifiers[getVarKindOffset(kind) + pos];
261   }
262 
263   ArrayRef<Identifier> getIds(VarKind kind) const {
264     assert(kind != VarKind::Local && "Local variables have no identifiers");
265     assert(usingIds && "Identifiers not enabled for space");
266     return {identifiers.data() + getVarKindOffset(kind), getNumVarKind(kind)};
267   }
268 
269   ArrayRef<Identifier> getIds() const {
270     assert(usingIds && "Identifiers not enabled for space");
271     return identifiers;
272   }
273 
274   /// Set the identifier of pos^th variable of the specified kind. Calls
275   /// resetIds if identifiers are not enabled.
276   void setId(VarKind kind, unsigned pos, Identifier id) {
277     assert(kind != VarKind::Local && "Local variables have no identifiers");
278     if (!usingIds)
279       resetIds();
280     identifiers[getVarKindOffset(kind) + pos] = id;
281   }
282 
283   /// Returns if identifiers are being used.
284   bool isUsingIds() const { return usingIds; }
285 
286   /// Reset the stored identifiers in the space. Enables `usingIds` if it was
287   /// `false` before.
288   void resetIds() {
289     identifiers.clear();
290     identifiers.resize(getNumDimAndSymbolVars());
291     usingIds = true;
292   }
293 
294   /// Disable identifiers being stored in space.
295   void disableIds() {
296     identifiers.clear();
297     usingIds = false;
298   }
299 
300   /// Check if the spaces are compatible, and the non-local variables having
301   /// same identifiers are in the same positions. If the space is not using
302   /// Identifiers, this check is same as isCompatible.
303   bool isAligned(const PresburgerSpace &other) const;
304   /// Same as above but only check the specified VarKind. Useful to check if
305   /// the symbols in two spaces are aligned.
306   bool isAligned(const PresburgerSpace &other, VarKind kind) const;
307 
308   /// Merge and align symbol variables of `this` and `other` with respect to
309   /// identifiers. After this operation the symbol variables of both spaces have
310   /// the same identifiers in the same order.
311   void mergeAndAlignSymbols(PresburgerSpace &other);
312 
313   void print(llvm::raw_ostream &os) const;
314   void dump() const;
315 
316 protected:
317   PresburgerSpace(unsigned numDomain, unsigned numRange, unsigned numSymbols,
318                   unsigned numLocals)
319       : numDomain(numDomain), numRange(numRange), numSymbols(numSymbols),
320         numLocals(numLocals) {}
321 
322 private:
323   // Number of variables corresponding to domain variables.
324   unsigned numDomain;
325 
326   // Number of variables corresponding to range variables.
327   unsigned numRange;
328 
329   /// Number of variables corresponding to symbols (unknown but constant for
330   /// analysis).
331   unsigned numSymbols;
332 
333   /// Number of variables corresponding to locals (variables corresponding
334   /// to existentially quantified variables).
335   unsigned numLocals;
336 
337   /// Stores whether or not identifiers are being used in this space.
338   bool usingIds = false;
339 
340   /// Stores an identifier for each non-local variable as a `void` pointer.
341   SmallVector<Identifier, 0> identifiers;
342 };
343 
344 } // namespace presburger
345 } // namespace mlir
346 
347 #endif // MLIR_ANALYSIS_PRESBURGER_PRESBURGERSPACE_H
348