MLIR 24.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
23namespace mlir {
24namespace presburger {
25using llvm::ArrayRef;
26using 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.
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.
71public:
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#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
106 LLVM_DUMP_METHOD void dump() const;
107#endif
108
109private:
110 /// The value of the identifier.
111 void *value = nullptr;
112
113#if LLVM_ENABLE_ABI_BREAKING_CHECKS
114 /// TypeID of the identifiers in space. This should be used in asserts only.
115 llvm::StringRef idType;
116#endif
117};
118
119/// PresburgerSpace is the space of all possible values of a tuple of integer
120/// valued variables/variables. Each variable has one of the three types:
121///
122/// Dimension: Ordinary variables over which the space is represented.
123///
124/// Symbol: Symbol variables correspond to fixed but unknown values.
125/// Mathematically, a space with symbolic variables is like a
126/// family of spaces indexed by the symbolic variables.
127///
128/// Local: Local variables correspond to existentially quantified variables.
129/// For example, consider the space: `(x, exists q)` where x is a dimension
130/// variable and q is a local variable. Let us put the constraints:
131/// `1 <= x <= 7, x = 2q`
132/// on this space to get the set:
133/// `(x) : (exists q : 1 <= x <= 7, x = 2q)`.
134/// An assignment to symbolic and dimension variables is valid if there
135/// exists some assignment to the local variable `q` satisfying these
136/// constraints. For this example, the set is equivalent to {2, 4, 6}.
137/// Mathematically, existential quantification can be thought of as the result
138/// of projection. In this example, `q` is existentially quantified. This can be
139/// thought of as the result of projecting out `q` from the previous example,
140/// i.e. we obtained {2, 4, 6} by projecting out the second dimension from
141/// {(2, 1), (4, 2), (6, 3)}.
142///
143/// Dimension variables are further divided into Domain and Range variables
144/// to support building relations.
145///
146/// Variables are stored in the following order:
147/// [Domain, Range, Symbols, Locals]
148///
149/// A space with no distinction between types of dimension variables can
150/// be implemented as a space with zero domain. VarKind::SetDim should be used
151/// to refer to dimensions in such spaces.
152///
153/// Compatibility of two spaces implies that number of variables of each kind
154/// other than Locals are equal. Equality of two spaces implies that number of
155/// variables of each kind are equal.
156///
157/// PresburgerSpace optionally also supports attaching an Identifier with each
158/// non-local variable in the space. This is disabled by default. `resetIds` is
159/// used to enable/reset these identifiers. The user can identify each variable
160/// in the space as corresponding to some Identifier. Some example use cases
161/// are described in the `Identifier` documentation above. The type attached to
162/// the Identifier can be different for different variables in the space.
164public:
165 static PresburgerSpace getRelationSpace(unsigned numDomain = 0,
166 unsigned numRange = 0,
167 unsigned numSymbols = 0,
168 unsigned numLocals = 0) {
169 return PresburgerSpace(numDomain, numRange, numSymbols, numLocals);
170 }
171
172 static PresburgerSpace getSetSpace(unsigned numDims = 0,
173 unsigned numSymbols = 0,
174 unsigned numLocals = 0) {
175 return PresburgerSpace(/*numDomain=*/0, /*numRange=*/numDims, numSymbols,
176 numLocals);
177 }
178
179 /// Get the domain/range space of this space. The returned space is a set
180 /// space.
183
184 /// Get the space without local variables.
186
187 unsigned getNumDomainVars() const { return numDomain; }
188 unsigned getNumRangeVars() const { return numRange; }
189 unsigned getNumSetDimVars() const { return numRange; }
190 unsigned getNumSymbolVars() const { return numSymbols; }
191 unsigned getNumLocalVars() const { return numLocals; }
192
193 unsigned getNumDimVars() const { return numDomain + numRange; }
194 unsigned getNumDimAndSymbolVars() const {
195 return numDomain + numRange + numSymbols;
196 }
197 unsigned getNumVars() const {
198 return numDomain + numRange + numSymbols + numLocals;
199 }
200
201 /// Get the number of vars of the specified kind.
202 unsigned getNumVarKind(VarKind kind) const;
203
204 /// Return the index at which the specified kind of var starts.
205 unsigned getVarKindOffset(VarKind kind) const;
206
207 /// Return the index at Which the specified kind of var ends.
208 unsigned getVarKindEnd(VarKind kind) const;
209
210 /// Get the number of elements of the specified kind in the range
211 /// [varStart, varLimit).
212 unsigned getVarKindOverlap(VarKind kind, unsigned varStart,
213 unsigned varLimit) const;
214
215 /// Return the VarKind of the var at the specified position.
216 VarKind getVarKindAt(unsigned pos) const;
217
218 /// Insert `num` variables of the specified kind at position `pos`.
219 /// Positions are relative to the kind of variable. Return the absolute
220 /// column position (i.e., not relative to the kind of variable) of the
221 /// first added variable.
222 ///
223 /// If identifiers are being used, the newly added variables have no
224 /// identifiers.
225 unsigned insertVar(VarKind kind, unsigned pos, unsigned num = 1);
226
227 /// Removes variables of the specified kind in the column range [varStart,
228 /// varLimit). The range is relative to the kind of variable.
229 void removeVarRange(VarKind kind, unsigned varStart, unsigned varLimit);
230
231 /// Converts variables of the specified kind in the column range [srcPos,
232 /// srcPos + num) to variables of the specified kind at position dstPos. The
233 /// ranges are relative to the kind of variable.
234 ///
235 /// srcKind and dstKind must be different.
236 void convertVarKind(VarKind srcKind, unsigned srcPos, unsigned num,
237 VarKind dstKind, unsigned dstPos);
238
239 /// Changes the partition between dimensions and symbols. Depending on the new
240 /// symbol count, either a chunk of dimensional variables immediately before
241 /// the split become symbols, or some of the symbols immediately after the
242 /// split become dimensions.
243 void setVarSymbolSeparation(unsigned newSymbolCount);
244
245 /// Swaps the posA^th variable of kindA and posB^th variable of kindB.
246 void swapVar(VarKind kindA, VarKind kindB, unsigned posA, unsigned posB);
247
248 /// Returns true if both the spaces are compatible i.e. if both spaces have
249 /// the same number of variables of each kind (excluding locals).
250 bool isCompatible(const PresburgerSpace &other) const;
251
252 /// Returns true if both the spaces are equal including local variables i.e.
253 /// if both spaces have the same number of variables of each kind (including
254 /// locals).
255 bool isEqual(const PresburgerSpace &other) const;
256
257 /// Get the identifier of pos^th variable of the specified kind.
258 Identifier getId(VarKind kind, unsigned pos) const {
259 assert(kind != VarKind::Local && "Local variables have no identifiers");
260 if (!usingIds)
261 return Identifier();
262 return identifiers[getVarKindOffset(kind) + pos];
263 }
264
266 assert(kind != VarKind::Local && "Local variables have no identifiers");
267 assert(usingIds && "Identifiers not enabled for space");
268 return {identifiers.data() + getVarKindOffset(kind), getNumVarKind(kind)};
269 }
270
272 assert(usingIds && "Identifiers not enabled for space");
273 return identifiers;
274 }
275
276 /// Set the identifier of pos^th variable of the specified kind. Calls
277 /// resetIds if identifiers are not enabled.
278 void setId(VarKind kind, unsigned pos, Identifier id) {
279 assert(kind != VarKind::Local && "Local variables have no identifiers");
280 if (!usingIds)
281 resetIds();
282 identifiers[getVarKindOffset(kind) + pos] = id;
283 }
284
285 /// Returns if identifiers are being used.
286 bool isUsingIds() const { return usingIds; }
287
288 /// Reset the stored identifiers in the space. Enables `usingIds` if it was
289 /// `false` before.
290 void resetIds() {
291 identifiers.clear();
292 identifiers.resize(getNumDimAndSymbolVars());
293 usingIds = true;
294 }
295
296 /// Disable identifiers being stored in space.
297 void disableIds() {
298 identifiers.clear();
299 usingIds = false;
300 }
301
302 /// Check if the spaces are compatible, and the non-local variables having
303 /// same identifiers are in the same positions. If the space is not using
304 /// Identifiers, this check is same as isCompatible.
305 bool isAligned(const PresburgerSpace &other) const;
306 /// Same as above but only check the specified VarKind. Useful to check if
307 /// the symbols in two spaces are aligned.
308 bool isAligned(const PresburgerSpace &other, VarKind kind) const;
309
310 /// Merge and align symbol variables of `this` and `other` with respect to
311 /// identifiers. After this operation the symbol variables of both spaces have
312 /// the same identifiers in the same order.
314
315 void print(llvm::raw_ostream &os) const;
316#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
317 LLVM_DUMP_METHOD void dump() const;
318#endif
319
320protected:
321 PresburgerSpace(unsigned numDomain, unsigned numRange, unsigned numSymbols,
322 unsigned numLocals)
323 : numDomain(numDomain), numRange(numRange), numSymbols(numSymbols),
324 numLocals(numLocals) {}
325
326private:
327 // Number of variables corresponding to domain variables.
328 unsigned numDomain;
329
330 // Number of variables corresponding to range variables.
331 unsigned numRange;
332
333 /// Number of variables corresponding to symbols (unknown but constant for
334 /// analysis).
335 unsigned numSymbols;
336
337 /// Number of variables corresponding to locals (variables corresponding
338 /// to existentially quantified variables).
339 unsigned numLocals;
340
341 /// Stores whether or not identifiers are being used in this space.
342 bool usingIds = false;
343
344 /// Stores an identifier for each non-local variable as a `void` pointer.
345 SmallVector<Identifier, 0> identifiers;
346};
347
348} // namespace presburger
349} // namespace mlir
350
351#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.
LLVM_DUMP_METHOD void dump() const
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.
PresburgerSpace getRangeSpace() const
LLVM_DUMP_METHOD void dump() 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).
ArrayRef< Identifier > getIds(VarKind kind) const
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.
ArrayRef< Identifier > getIds() const
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)
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.