1 //===- Utils.h - General utilities for Presburger library ------*- 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 // Utility functions required by the Presburger Library. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef MLIR_ANALYSIS_PRESBURGER_UTILS_H 14 #define MLIR_ANALYSIS_PRESBURGER_UTILS_H 15 16 #include "mlir/Analysis/Presburger/Matrix.h" 17 #include "llvm/ADT/DynamicAPInt.h" 18 #include "llvm/ADT/STLExtras.h" 19 #include "llvm/ADT/SmallBitVector.h" 20 #include "llvm/Support/raw_ostream.h" 21 #include <optional> 22 #include <string> 23 24 namespace mlir { 25 namespace presburger { 26 class IntegerRelation; 27 28 /// This class represents the result of operations optimizing something subject 29 /// to some constraints. If the constraints were not satisfiable the, kind will 30 /// be Empty. If the optimum is unbounded, the kind is Unbounded, and if the 31 /// optimum is bounded, the kind will be Bounded and `optimum` holds the optimal 32 /// value. 33 enum class OptimumKind { Empty, Unbounded, Bounded }; 34 template <typename T> 35 class MaybeOptimum { 36 public: 37 private: 38 OptimumKind kind = OptimumKind::Empty; 39 T optimum; 40 41 public: 42 MaybeOptimum() = default; 43 MaybeOptimum(OptimumKind kind) : kind(kind) { 44 assert(kind != OptimumKind::Bounded && 45 "Bounded optima should be constructed by specifying the optimum!"); 46 } 47 MaybeOptimum(const T &optimum) 48 : kind(OptimumKind::Bounded), optimum(optimum) {} 49 50 OptimumKind getKind() const { return kind; } 51 bool isBounded() const { return kind == OptimumKind::Bounded; } 52 bool isUnbounded() const { return kind == OptimumKind::Unbounded; } 53 bool isEmpty() const { return kind == OptimumKind::Empty; } 54 55 std::optional<T> getOptimumIfBounded() const { return optimum; } 56 const T &getBoundedOptimum() const { 57 assert(kind == OptimumKind::Bounded && 58 "This should be called only for bounded optima"); 59 return optimum; 60 } 61 T &getBoundedOptimum() { 62 assert(kind == OptimumKind::Bounded && 63 "This should be called only for bounded optima"); 64 return optimum; 65 } 66 const T &operator*() const { return getBoundedOptimum(); } 67 T &operator*() { return getBoundedOptimum(); } 68 const T *operator->() const { return &getBoundedOptimum(); } 69 T *operator->() { return &getBoundedOptimum(); } 70 bool operator==(const MaybeOptimum<T> &other) const { 71 if (kind != other.kind) 72 return false; 73 if (kind != OptimumKind::Bounded) 74 return true; 75 return optimum == other.optimum; 76 } 77 78 // Given f that takes a T and returns a U, convert this `MaybeOptimum<T>` to 79 // a `MaybeOptimum<U>` by applying `f` to the bounded optimum if it exists, or 80 // returning a MaybeOptimum of the same kind otherwise. 81 template <class Function> 82 auto map(const Function &f) const & -> MaybeOptimum<decltype(f(optimum))> { 83 if (kind == OptimumKind::Bounded) 84 return f(optimum); 85 return kind; 86 } 87 }; 88 89 /// `ReprKind` enum is used to set the constraint type in `MaybeLocalRepr`. 90 enum class ReprKind { Inequality, Equality, None }; 91 92 /// `MaybeLocalRepr` contains the indices of the constraints that can be 93 /// expressed as a floordiv of an affine function. If it's an `equality` 94 /// constraint, `equalityIdx` is set, in case of `inequality` the 95 /// `lowerBoundIdx` and `upperBoundIdx` is set. By default the kind attribute is 96 /// set to None. 97 struct MaybeLocalRepr { 98 ReprKind kind = ReprKind::None; 99 explicit operator bool() const { return kind != ReprKind::None; } 100 union { 101 unsigned equalityIdx; 102 struct { 103 unsigned lowerBoundIdx, upperBoundIdx; 104 } inequalityPair; 105 } repr; 106 }; 107 108 /// Class storing division representation of local variables of a constraint 109 /// system. The coefficients of the dividends are stored in order: 110 /// [nonLocalVars, localVars, constant]. Each local variable may or may not have 111 /// a representation. If the local does not have a representation, the dividend 112 /// of the division has no meaning and the denominator is zero. If it has a 113 /// representation, the denominator will be positive. 114 /// 115 /// The i^th division here, represents the division representation of the 116 /// variable at position `divOffset + i` in the constraint system. 117 class DivisionRepr { 118 public: 119 DivisionRepr(unsigned numVars, unsigned numDivs) 120 : dividends(numDivs, numVars + 1), denoms(numDivs, DynamicAPInt(0)) {} 121 122 DivisionRepr(unsigned numVars) : dividends(0, numVars + 1) {} 123 124 unsigned getNumVars() const { return dividends.getNumColumns() - 1; } 125 unsigned getNumDivs() const { return dividends.getNumRows(); } 126 unsigned getNumNonDivs() const { return getNumVars() - getNumDivs(); } 127 // Get the offset from where division variables start. 128 unsigned getDivOffset() const { return getNumVars() - getNumDivs(); } 129 130 // Check whether the `i^th` division has a division representation or not. 131 bool hasRepr(unsigned i) const { return denoms[i] != 0; } 132 // Check whether all the divisions have a division representation or not. 133 bool hasAllReprs() const { return !llvm::is_contained(denoms, 0); } 134 135 // Clear the division representation of the i^th local variable. 136 void clearRepr(unsigned i) { denoms[i] = 0; } 137 138 // Get the dividend of the `i^th` division. 139 MutableArrayRef<DynamicAPInt> getDividend(unsigned i) { 140 return dividends.getRow(i); 141 } 142 ArrayRef<DynamicAPInt> getDividend(unsigned i) const { 143 return dividends.getRow(i); 144 } 145 146 // For a given point containing values for each variable other than the 147 // division variables, try to find the values for each division variable from 148 // their division representation. 149 SmallVector<std::optional<DynamicAPInt>, 4> 150 divValuesAt(ArrayRef<DynamicAPInt> point) const; 151 152 // Get the `i^th` denominator. 153 DynamicAPInt &getDenom(unsigned i) { return denoms[i]; } 154 DynamicAPInt getDenom(unsigned i) const { return denoms[i]; } 155 156 ArrayRef<DynamicAPInt> getDenoms() const { return denoms; } 157 158 void setDiv(unsigned i, ArrayRef<DynamicAPInt> dividend, 159 const DynamicAPInt &divisor) { 160 dividends.setRow(i, dividend); 161 denoms[i] = divisor; 162 } 163 164 // Find the greatest common divisor (GCD) of the dividends and divisor for 165 // each valid division. Divide the dividends and divisor by the GCD to 166 // simplify the expression. 167 void normalizeDivs(); 168 169 void insertDiv(unsigned pos, ArrayRef<DynamicAPInt> dividend, 170 const DynamicAPInt &divisor); 171 void insertDiv(unsigned pos, unsigned num = 1); 172 173 /// Removes duplicate divisions. On every possible duplicate division found, 174 /// `merge(i, j)`, where `i`, `j` are current index of the duplicate 175 /// divisions, is called and division at index `j` is merged into division at 176 /// index `i`. If `merge(i, j)` returns `true`, the divisions are merged i.e. 177 /// `j^th` division gets eliminated and it's each instance is replaced by 178 /// `i^th` division. If it returns `false`, the divisions are not merged. 179 /// `merge` can also do side effects, For example it can merge the local 180 /// variables in IntegerRelation. 181 void 182 removeDuplicateDivs(llvm::function_ref<bool(unsigned i, unsigned j)> merge); 183 184 void print(raw_ostream &os) const; 185 void dump() const; 186 187 private: 188 /// Each row of the Matrix represents a single division dividend. The 189 /// `i^th` row represents the dividend of the variable at `divOffset + i` 190 /// in the constraint system (and the `i^th` local variable). 191 IntMatrix dividends; 192 193 /// Denominators of each division. If a denominator of a division is `0`, the 194 /// division variable is considered to not have a division representation. 195 /// Otherwise, the denominator is positive. 196 SmallVector<DynamicAPInt, 4> denoms; 197 }; 198 199 /// If `q` is defined to be equal to `expr floordiv d`, this equivalent to 200 /// saying that `q` is an integer and `q` is subject to the inequalities 201 /// `0 <= expr - d*q <= c - 1` (quotient remainder theorem). 202 /// 203 /// Rearranging, we get the bounds on `q`: d*q <= expr <= d*q + d - 1. 204 /// 205 /// `getDivUpperBound` returns `d*q <= expr`, and 206 /// `getDivLowerBound` returns `expr <= d*q + d - 1`. 207 /// 208 /// The parameter `dividend` corresponds to `expr` above, `divisor` to `d`, and 209 /// `localVarIdx` to the position of `q` in the coefficient list. 210 /// 211 /// The coefficient of `q` in `dividend` must be zero, as it is not allowed for 212 /// local variable to be a floor division of an expression involving itself. 213 /// The divisor must be positive. 214 SmallVector<DynamicAPInt, 8> getDivUpperBound(ArrayRef<DynamicAPInt> dividend, 215 const DynamicAPInt &divisor, 216 unsigned localVarIdx); 217 SmallVector<DynamicAPInt, 8> getDivLowerBound(ArrayRef<DynamicAPInt> dividend, 218 const DynamicAPInt &divisor, 219 unsigned localVarIdx); 220 221 llvm::SmallBitVector getSubrangeBitVector(unsigned len, unsigned setOffset, 222 unsigned numSet); 223 224 /// Check if the pos^th variable can be expressed as a floordiv of an affine 225 /// function of other variables (where the divisor is a positive constant). 226 /// `foundRepr` contains a boolean for each variable indicating if the 227 /// explicit representation for that variable has already been computed. 228 /// Return the given array as an array of DynamicAPInts. 229 SmallVector<DynamicAPInt, 8> getDynamicAPIntVec(ArrayRef<int64_t> range); 230 /// Return the given array as an array of int64_t. 231 SmallVector<int64_t, 8> getInt64Vec(ArrayRef<DynamicAPInt> range); 232 233 /// Returns the `MaybeLocalRepr` struct which contains the indices of the 234 /// constraints that can be expressed as a floordiv of an affine function. If 235 /// the representation could be computed, `dividend` and `divisor` are set, 236 /// in which case, denominator will be positive. If the representation could 237 /// not be computed, the kind attribute in `MaybeLocalRepr` is set to None. 238 MaybeLocalRepr computeSingleVarRepr(const IntegerRelation &cst, 239 ArrayRef<bool> foundRepr, unsigned pos, 240 MutableArrayRef<DynamicAPInt> dividend, 241 DynamicAPInt &divisor); 242 243 /// The following overload using int64_t is required for a callsite in 244 /// AffineStructures.h. 245 MaybeLocalRepr computeSingleVarRepr(const IntegerRelation &cst, 246 ArrayRef<bool> foundRepr, unsigned pos, 247 SmallVector<int64_t, 8> ÷nd, 248 unsigned &divisor); 249 250 /// Given two relations, A and B, add additional local vars to the sets such 251 /// that both have the union of the local vars in each set, without changing 252 /// the set of points that lie in A and B. 253 /// 254 /// While taking union, if a local var in any set has a division representation 255 /// which is a duplicate of division representation, of another local var in any 256 /// set, it is not added to the final union of local vars and is instead merged. 257 /// 258 /// On every possible merge, `merge(i, j)` is called. `i`, `j` are position 259 /// of local variables in both sets which are being merged. If `merge(i, j)` 260 /// returns true, the divisions are merged, otherwise the divisions are not 261 /// merged. 262 void mergeLocalVars(IntegerRelation &relA, IntegerRelation &relB, 263 llvm::function_ref<bool(unsigned i, unsigned j)> merge); 264 265 /// Compute the gcd of the range. 266 DynamicAPInt gcdRange(ArrayRef<DynamicAPInt> range); 267 268 /// Divide the range by its gcd and return the gcd. 269 DynamicAPInt normalizeRange(MutableArrayRef<DynamicAPInt> range); 270 271 /// Normalize the given (numerator, denominator) pair by dividing out the 272 /// common factors between them. The numerator here is an affine expression 273 /// with integer coefficients. The denominator must be positive. 274 void normalizeDiv(MutableArrayRef<DynamicAPInt> num, DynamicAPInt &denom); 275 276 /// Return `coeffs` with all the elements negated. 277 SmallVector<DynamicAPInt, 8> getNegatedCoeffs(ArrayRef<DynamicAPInt> coeffs); 278 279 /// Return the complement of the given inequality. 280 /// 281 /// The complement of a_1 x_1 + ... + a_n x_ + c >= 0 is 282 /// a_1 x_1 + ... + a_n x_ + c < 0, i.e., -a_1 x_1 - ... - a_n x_ - c - 1 >= 0, 283 /// since all the variables are constrained to be integers. 284 SmallVector<DynamicAPInt, 8> getComplementIneq(ArrayRef<DynamicAPInt> ineq); 285 286 /// Compute the dot product of two vectors. 287 /// The vectors must have the same sizes. 288 Fraction dotProduct(ArrayRef<Fraction> a, ArrayRef<Fraction> b); 289 290 /// Find the product of two polynomials, each given by an array of 291 /// coefficients. 292 std::vector<Fraction> multiplyPolynomials(ArrayRef<Fraction> a, 293 ArrayRef<Fraction> b); 294 295 bool isRangeZero(ArrayRef<Fraction> arr); 296 297 /// Example usage: 298 /// Print .12, 3.4, 56.7 299 /// preAlign = ".", minSpacing = 1, 300 /// .12 .12 301 /// 3.4 3.4 302 /// 56.7 56.7 303 struct PrintTableMetrics { 304 // If unknown, set to 0 and pass the struct into updatePrintMetrics. 305 unsigned maxPreIndent; 306 unsigned maxPostIndent; 307 std::string preAlign; 308 }; 309 310 /// Iterate over each val in the table and update 'm' where 311 /// .maxPreIndent and .maxPostIndent are initialized to 0. 312 /// class T is any type that can be handled by llvm::raw_string_ostream. 313 template <class T> 314 void updatePrintMetrics(T val, PrintTableMetrics &m) { 315 std::string str; 316 llvm::raw_string_ostream(str) << val; 317 if (str.empty()) 318 return; 319 unsigned preIndent = str.find(m.preAlign); 320 preIndent = (preIndent != (unsigned)std::string::npos) ? preIndent + 1 : 0; 321 m.maxPreIndent = std::max(m.maxPreIndent, preIndent); 322 m.maxPostIndent = 323 std::max(m.maxPostIndent, (unsigned int)(str.length() - preIndent)); 324 } 325 326 /// Print val in the table with metrics specified in 'm'. 327 template <class T> 328 void printWithPrintMetrics(raw_ostream &os, T val, unsigned minSpacing, 329 const PrintTableMetrics &m) { 330 std::string str; 331 llvm::raw_string_ostream(str) << val; 332 unsigned preIndent; 333 if (!str.empty()) { 334 preIndent = str.find(m.preAlign); 335 preIndent = (preIndent != (unsigned)std::string::npos) ? preIndent + 1 : 0; 336 } else { 337 preIndent = 0; 338 } 339 for (unsigned i = 0; i < (minSpacing + m.maxPreIndent - preIndent); ++i) 340 os << " "; 341 os << str; 342 for (unsigned i = 0; i < m.maxPostIndent - (str.length() - preIndent); ++i) 343 os << " "; 344 } 345 } // namespace presburger 346 } // namespace mlir 347 348 #endif // MLIR_ANALYSIS_PRESBURGER_UTILS_H 349