MLIR  20.0.0git
PresburgerSpace.h
Go to the documentation of this file.
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.
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.
181 
182  /// Get the space without local variables.
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 
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 
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.
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
An Identifier stores a pointer to an object, such as a Value or an Operation.
T getValue() const
Get the value of the identifier casted to type T.
bool operator!=(const Identifier &other) const
bool isEqual(const Identifier &other) const
Check if the two identifiers are equal.
void print(llvm::raw_ostream &os) const
bool operator==(const Identifier &other) const
PresburgerSpace is the space of all possible values of a tuple of integer valued variables/variables.
ArrayRef< Identifier > getIds() const
PresburgerSpace getRangeSpace() const
void setId(VarKind kind, unsigned pos, Identifier id)
Set the identifier of pos^th variable of the specified kind.
unsigned getNumVarKind(VarKind kind) const
Get the number of vars of the specified kind.
void resetIds()
Reset the stored identifiers in the space.
bool isEqual(const PresburgerSpace &other) const
Returns true if both the spaces are equal including local variables i.e.
PresburgerSpace getDomainSpace() const
Get the domain/range space of this space.
void disableIds()
Disable identifiers being stored in space.
bool isUsingIds() const
Returns if identifiers are being used.
void convertVarKind(VarKind srcKind, unsigned srcPos, unsigned num, VarKind dstKind, unsigned dstPos)
Converts variables of the specified kind in the column range [srcPos, srcPos + num) to variables of t...
void removeVarRange(VarKind kind, unsigned varStart, unsigned varLimit)
Removes variables of the specified kind in the column range [varStart, varLimit).
unsigned getVarKindOffset(VarKind kind) const
Return the index at which the specified kind of var starts.
unsigned getVarKindEnd(VarKind kind) const
Return the index at Which the specified kind of var ends.
Identifier getId(VarKind kind, unsigned pos) const
Get the identifier of pos^th variable of the specified kind.
PresburgerSpace(unsigned numDomain, unsigned numRange, unsigned numSymbols, unsigned numLocals)
VarKind getVarKindAt(unsigned pos) const
Return the VarKind of the var at the specified position.
unsigned getVarKindOverlap(VarKind kind, unsigned varStart, unsigned varLimit) const
Get the number of elements of the specified kind in the range [varStart, varLimit).
static PresburgerSpace getSetSpace(unsigned numDims=0, unsigned numSymbols=0, unsigned numLocals=0)
ArrayRef< Identifier > getIds(VarKind kind) const
bool isCompatible(const PresburgerSpace &other) const
Returns true if both the spaces are compatible i.e.
void print(llvm::raw_ostream &os) const
void mergeAndAlignSymbols(PresburgerSpace &other)
Merge and align symbol variables of this and other with respect to identifiers.
void setVarSymbolSeparation(unsigned newSymbolCount)
Changes the partition between dimensions and symbols.
bool isAligned(const PresburgerSpace &other) const
Check if the spaces are compatible, and the non-local variables having same identifiers are in the sa...
PresburgerSpace getSpaceWithoutLocals() const
Get the space without local variables.
static PresburgerSpace getRelationSpace(unsigned numDomain=0, unsigned numRange=0, unsigned numSymbols=0, unsigned numLocals=0)
void swapVar(VarKind kindA, VarKind kindB, unsigned posA, unsigned posB)
Swaps the posA^th variable of kindA and posB^th variable of kindB.
unsigned insertVar(VarKind kind, unsigned pos, unsigned num=1)
Insert num variables of the specified kind at position pos.
The OpAsmOpInterface, see OpAsmInterface.td for more details.
Definition: CallGraph.h:229
VarKind
Kind of variable.
Include the generated interface declarations.
Represents a range (offset, size, and stride) where each element of the triple may be dynamic or stat...