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