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