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 "mlir/Support/TypeID.h"
18 #include "llvm/ADT/ArrayRef.h"
19 #include "llvm/Support/ErrorHandling.h"
20 #include "llvm/Support/PointerLikeTypeTraits.h"
21 #include "llvm/Support/raw_ostream.h"
22 
23 namespace mlir {
24 namespace presburger {
25 
26 /// Kind of variable. Implementation wise SetDims are treated as Range
27 /// vars, and spaces with no distinction between dimension vars are treated
28 /// as relations with zero domain vars.
29 enum class VarKind { Symbol, Local, Domain, Range, SetDim = Range };
30 
31 /// PresburgerSpace is the space of all possible values of a tuple of integer
32 /// valued variables/variables. Each variable has one of the three types:
33 ///
34 /// Dimension: Ordinary variables over which the space is represented.
35 ///
36 /// Symbol: Symbol variables correspond to fixed but unknown values.
37 /// Mathematically, a space with symbolic variables is like a
38 /// family of spaces indexed by the symbolic variables.
39 ///
40 /// Local: Local variables correspond to existentially quantified variables.
41 /// For example, consider the space: `(x, exists q)` where x is a dimension
42 /// variable and q is a local variable. Let us put the constraints:
43 ///       `1 <= x <= 7, x = 2q`
44 /// on this space to get the set:
45 ///       `(x) : (exists q : q <= x <= 7, x = 2q)`.
46 /// An assignment to symbolic and dimension variables is valid if there
47 /// exists some assignment to the local variable `q` satisfying these
48 /// constraints. For this example, the set is equivalent to {2, 4, 6}.
49 /// Mathematically, existential quantification can be thought of as the result
50 /// of projection. In this example, `q` is existentially quantified. This can be
51 /// thought of as the result of projecting out `q` from the previous example,
52 /// i.e. we obtained {2, 4, 6} by projecting out the second dimension from
53 /// {(2, 1), (4, 2), (6, 2)}.
54 ///
55 /// Dimension variables are further divided into Domain and Range variables
56 /// to support building relations.
57 ///
58 /// Variables are stored in the following order:
59 ///       [Domain, Range, Symbols, Locals]
60 ///
61 /// A space with no distinction between types of dimension variables can
62 /// be implemented as a space with zero domain. VarKind::SetDim should be used
63 /// to refer to dimensions in such spaces.
64 ///
65 /// Compatibility of two spaces implies that number of variables of each kind
66 /// other than Locals are equal. Equality of two spaces implies that number of
67 /// variables of each kind are equal.
68 ///
69 /// PresburgerSpace optionally also supports attaching some information to each
70 /// variable in space, called "identifier" of that variable. `resetIds<IdType>`
71 /// is used to enable/reset these identifiers. All identifiers must be of the
72 /// same type, `IdType`. `IdType` must have a `llvm::PointerLikeTypeTraits`
73 /// specialization available and should be supported via `mlir::TypeID`.
74 ///
75 /// These identifiers can be used to check if two variables in two different
76 /// spaces are actually same variable.
77 class PresburgerSpace {
78 public:
79   static PresburgerSpace getRelationSpace(unsigned numDomain = 0,
80                                           unsigned numRange = 0,
81                                           unsigned numSymbols = 0,
82                                           unsigned numLocals = 0) {
83     return PresburgerSpace(numDomain, numRange, numSymbols, numLocals);
84   }
85 
86   static PresburgerSpace getSetSpace(unsigned numDims = 0,
87                                      unsigned numSymbols = 0,
88                                      unsigned numLocals = 0) {
89     return PresburgerSpace(/*numDomain=*/0, /*numRange=*/numDims, numSymbols,
90                            numLocals);
91   }
92 
getNumDomainVars()93   unsigned getNumDomainVars() const { return numDomain; }
getNumRangeVars()94   unsigned getNumRangeVars() const { return numRange; }
getNumSetDimVars()95   unsigned getNumSetDimVars() const { return numRange; }
getNumSymbolVars()96   unsigned getNumSymbolVars() const { return numSymbols; }
getNumLocalVars()97   unsigned getNumLocalVars() const { return numLocals; }
98 
getNumDimVars()99   unsigned getNumDimVars() const { return numDomain + numRange; }
getNumDimAndSymbolVars()100   unsigned getNumDimAndSymbolVars() const {
101     return numDomain + numRange + numSymbols;
102   }
getNumVars()103   unsigned getNumVars() const {
104     return numDomain + numRange + numSymbols + numLocals;
105   }
106 
107   /// Get the number of vars of the specified kind.
108   unsigned getNumVarKind(VarKind kind) const;
109 
110   /// Return the index at which the specified kind of var starts.
111   unsigned getVarKindOffset(VarKind kind) const;
112 
113   /// Return the index at Which the specified kind of var ends.
114   unsigned getVarKindEnd(VarKind kind) const;
115 
116   /// Get the number of elements of the specified kind in the range
117   /// [varStart, varLimit).
118   unsigned getVarKindOverlap(VarKind kind, unsigned varStart,
119                              unsigned varLimit) const;
120 
121   /// Return the VarKind of the var at the specified position.
122   VarKind getVarKindAt(unsigned pos) const;
123 
124   /// Insert `num` variables of the specified kind at position `pos`.
125   /// Positions are relative to the kind of variable. Return the absolute
126   /// column position (i.e., not relative to the kind of variable) of the
127   /// first added variable.
128   ///
129   /// If identifiers are being used, the newly added variables have no
130   /// identifiers.
131   unsigned insertVar(VarKind kind, unsigned pos, unsigned num = 1);
132 
133   /// Removes variables of the specified kind in the column range [varStart,
134   /// varLimit). The range is relative to the kind of variable.
135   void removeVarRange(VarKind kind, unsigned varStart, unsigned varLimit);
136 
137   /// Swaps the posA^th variable of kindA and posB^th variable of kindB.
138   void swapVar(VarKind kindA, VarKind kindB, unsigned posA, unsigned posB);
139 
140   /// Returns true if both the spaces are compatible i.e. if both spaces have
141   /// the same number of variables of each kind (excluding locals).
142   bool isCompatible(const PresburgerSpace &other) const;
143 
144   /// Returns true if both the spaces are equal including local variables i.e.
145   /// if both spaces have the same number of variables of each kind (including
146   /// locals).
147   bool isEqual(const PresburgerSpace &other) const;
148 
149   /// Changes the partition between dimensions and symbols. Depending on the new
150   /// symbol count, either a chunk of dimensional variables immediately before
151   /// the split become symbols, or some of the symbols immediately after the
152   /// split become dimensions.
153   void setVarSymbolSeperation(unsigned newSymbolCount);
154 
155   void print(llvm::raw_ostream &os) const;
156   void dump() const;
157 
158   //===--------------------------------------------------------------------===//
159   //     Identifier Interactions
160   //===--------------------------------------------------------------------===//
161 
162   /// Set the identifier for `i^th` variable to `id`. `T` here should match the
163   /// type used to enable identifiers.
164   template <typename T>
setId(VarKind kind,unsigned i,T id)165   void setId(VarKind kind, unsigned i, T id) {
166 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
167     assert(TypeID::get<T>() == idType && "Type mismatch");
168 #endif
169     atId(kind, i) = llvm::PointerLikeTypeTraits<T>::getAsVoidPointer(id);
170   }
171 
172   /// Get the identifier for `i^th` variable casted to type `T`. `T` here
173   /// should match the type used to enable identifiers.
174   template <typename T>
getId(VarKind kind,unsigned i)175   T getId(VarKind kind, unsigned i) const {
176 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
177     assert(TypeID::get<T>() == idType && "Type mismatch");
178 #endif
179     return llvm::PointerLikeTypeTraits<T>::getFromVoidPointer(atId(kind, i));
180   }
181 
182   /// Check if the i^th variable of the specified kind has a non-null
183   /// identifier.
hasId(VarKind kind,unsigned i)184   bool hasId(VarKind kind, unsigned i) const {
185     return atId(kind, i) != nullptr;
186   }
187 
188   /// Check if the spaces are compatible, as well as have the same identifiers
189   /// for each variable.
190   bool isAligned(const PresburgerSpace &other) const;
191   /// Check if the number of variables of the specified kind match, and have
192   /// same identifiers with the other space.
193   bool isAligned(const PresburgerSpace &other, VarKind kind) const;
194 
195   /// Find the variable of the specified kind with identifier `id`.
196   /// Returns PresburgerSpace::kIdNotFound if identifier is not found.
197   template <typename T>
findId(VarKind kind,T id)198   unsigned findId(VarKind kind, T id) const {
199     unsigned i = 0;
200     for (unsigned e = getNumVarKind(kind); i < e; ++i)
201       if (hasId(kind, i) && getId<T>(kind, i) == id)
202         return i;
203     return kIdNotFound;
204   }
205   static const unsigned kIdNotFound = UINT_MAX;
206 
207   /// Returns if identifiers are being used.
isUsingIds()208   bool isUsingIds() const { return usingIds; }
209 
210   /// Reset the stored identifiers in the space. Enables `usingIds` if it was
211   /// `false` before.
212   template <typename T>
resetIds()213   void resetIds() {
214     identifiers.clear();
215     identifiers.resize(getNumDimAndSymbolVars());
216 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
217     idType = TypeID::get<T>();
218 #endif
219 
220     usingIds = true;
221   }
222 
223   /// Disable identifiers being stored in space.
disableIds()224   void disableIds() {
225     identifiers.clear();
226     usingIds = false;
227   }
228 
229 protected:
230   PresburgerSpace(unsigned numDomain = 0, unsigned numRange = 0,
231                   unsigned numSymbols = 0, unsigned numLocals = 0)
numDomain(numDomain)232       : numDomain(numDomain), numRange(numRange), numSymbols(numSymbols),
233         numLocals(numLocals) {}
234 
atId(VarKind kind,unsigned i)235   void *&atId(VarKind kind, unsigned i) {
236     assert(usingIds && "Cannot access identifiers when `usingIds` is false.");
237     assert(kind != VarKind::Local &&
238            "Local variables cannot have identifiers.");
239     return identifiers[getVarKindOffset(kind) + i];
240   }
241 
atId(VarKind kind,unsigned i)242   void *atId(VarKind kind, unsigned i) const {
243     assert(usingIds && "Cannot access identifiers when `usingIds` is false.");
244     assert(kind != VarKind::Local &&
245            "Local variables cannot have identifiers.");
246     return identifiers[getVarKindOffset(kind) + i];
247   }
248 
249 private:
250   // Number of variables corresponding to domain variables.
251   unsigned numDomain;
252 
253   // Number of variables corresponding to range variables.
254   unsigned numRange;
255 
256   /// Number of variables corresponding to symbols (unknown but constant for
257   /// analysis).
258   unsigned numSymbols;
259 
260   /// Number of variables corresponding to locals (variables corresponding
261   /// to existentially quantified variables).
262   unsigned numLocals;
263 
264   /// Stores whether or not identifiers are being used in this space.
265   bool usingIds = false;
266 
267 #ifdef LLVM_ENABLE_ABI_BREAKING_CHECKS
268   /// TypeID of the identifiers in space. This should be used in asserts only.
269   TypeID idType;
270 #endif
271 
272   /// Stores an identifier for each non-local variable as a `void` pointer.
273   SmallVector<void *, 0> identifiers;
274 };
275 
276 } // namespace presburger
277 } // namespace mlir
278 
279 #endif // MLIR_ANALYSIS_PRESBURGER_PRESBURGERSPACE_H
280