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/MPInt.h" 17 #include "mlir/Support/LLVM.h" 18 #include "llvm/ADT/STLExtras.h" 19 #include "llvm/ADT/SmallBitVector.h" 20 21 #include "mlir/Analysis/Presburger/Matrix.h" 22 23 namespace mlir { 24 namespace presburger { 25 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; MaybeOptimum(OptimumKind kind)43 MaybeOptimum(OptimumKind kind) : kind(kind) { 44 assert(kind != OptimumKind::Bounded && 45 "Bounded optima should be constructed by specifying the optimum!"); 46 } MaybeOptimum(const T & optimum)47 MaybeOptimum(const T &optimum) 48 : kind(OptimumKind::Bounded), optimum(optimum) {} 49 getKind()50 OptimumKind getKind() const { return kind; } isBounded()51 bool isBounded() const { return kind == OptimumKind::Bounded; } isUnbounded()52 bool isUnbounded() const { return kind == OptimumKind::Unbounded; } isEmpty()53 bool isEmpty() const { return kind == OptimumKind::Empty; } 54 getOptimumIfBounded()55 Optional<T> getOptimumIfBounded() const { return optimum; } getBoundedOptimum()56 const T &getBoundedOptimum() const { 57 assert(kind == OptimumKind::Bounded && 58 "This should be called only for bounded optima"); 59 return optimum; 60 } getBoundedOptimum()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. 113 /// 114 /// The i^th division here, represents the division representation of the 115 /// variable at position `divOffset + i` in the constraint system. 116 class DivisionRepr { 117 public: DivisionRepr(unsigned numVars,unsigned numDivs)118 DivisionRepr(unsigned numVars, unsigned numDivs) 119 : dividends(numDivs, numVars + 1), denoms(numDivs, 0) {} 120 DivisionRepr(unsigned numVars)121 DivisionRepr(unsigned numVars) : dividends(numVars + 1, 0) {} 122 getNumVars()123 unsigned getNumVars() const { return dividends.getNumColumns() - 1; } getNumDivs()124 unsigned getNumDivs() const { return dividends.getNumRows(); } getNumNonDivs()125 unsigned getNumNonDivs() const { return getNumVars() - getNumDivs(); } 126 // Get the offset from where division variables start. getDivOffset()127 unsigned getDivOffset() const { return getNumVars() - getNumDivs(); } 128 129 // Check whether the `i^th` division has a division representation or not. hasRepr(unsigned i)130 bool hasRepr(unsigned i) const { return denoms[i] != 0; } 131 // Check whether all the divisions have a division representation or not. hasAllReprs()132 bool hasAllReprs() const { 133 return all_of(denoms, [](unsigned denom) { return denom != 0; }); 134 } 135 136 // Clear the division representation of the i^th local variable. clearRepr(unsigned i)137 void clearRepr(unsigned i) { denoms[i] = 0; } 138 139 // Get the dividend of the `i^th` division. getDividend(unsigned i)140 MutableArrayRef<int64_t> getDividend(unsigned i) { 141 return dividends.getRow(i); 142 } getDividend(unsigned i)143 ArrayRef<int64_t> getDividend(unsigned i) const { 144 return dividends.getRow(i); 145 } 146 147 // Get the `i^th` denominator. getDenom(unsigned i)148 unsigned &getDenom(unsigned i) { return denoms[i]; } getDenom(unsigned i)149 unsigned getDenom(unsigned i) const { return denoms[i]; } 150 getDenoms()151 ArrayRef<unsigned> getDenoms() const { return denoms; } 152 setDividend(unsigned i,ArrayRef<int64_t> dividend)153 void setDividend(unsigned i, ArrayRef<int64_t> dividend) { 154 dividends.setRow(i, dividend); 155 } 156 157 /// Removes duplicate divisions. On every possible duplicate division found, 158 /// `merge(i, j)`, where `i`, `j` are current index of the duplicate 159 /// divisions, is called and division at index `j` is merged into division at 160 /// index `i`. If `merge(i, j)` returns `true`, the divisions are merged i.e. 161 /// `j^th` division gets eliminated and it's each instance is replaced by 162 /// `i^th` division. If it returns `false`, the divisions are not merged. 163 /// `merge` can also do side effects, For example it can merge the local 164 /// variables in IntegerRelation. 165 void 166 removeDuplicateDivs(llvm::function_ref<bool(unsigned i, unsigned j)> merge); 167 168 void print(raw_ostream &os) const; 169 void dump() const; 170 171 private: 172 /// Each row of the Matrix represents a single division dividend. The 173 /// `i^th` row represents the dividend of the variable at `divOffset + i` 174 /// in the constraint system (and the `i^th` local variable). 175 Matrix dividends; 176 177 /// Denominators of each division. If a denominator of a division is `0`, the 178 /// division variable is considered to not have a division representation. 179 SmallVector<unsigned, 4> denoms; 180 }; 181 182 /// If `q` is defined to be equal to `expr floordiv d`, this equivalent to 183 /// saying that `q` is an integer and `q` is subject to the inequalities 184 /// `0 <= expr - d*q <= c - 1` (quotient remainder theorem). 185 /// 186 /// Rearranging, we get the bounds on `q`: d*q <= expr <= d*q + d - 1. 187 /// 188 /// `getDivUpperBound` returns `d*q <= expr`, and 189 /// `getDivLowerBound` returns `expr <= d*q + d - 1`. 190 /// 191 /// The parameter `dividend` corresponds to `expr` above, `divisor` to `d`, and 192 /// `localVarIdx` to the position of `q` in the coefficient list. 193 /// 194 /// The coefficient of `q` in `dividend` must be zero, as it is not allowed for 195 /// local variable to be a floor division of an expression involving itself. 196 SmallVector<int64_t, 8> getDivUpperBound(ArrayRef<int64_t> dividend, 197 int64_t divisor, unsigned localVarIdx); 198 SmallVector<int64_t, 8> getDivLowerBound(ArrayRef<int64_t> dividend, 199 int64_t divisor, unsigned localVarIdx); 200 201 llvm::SmallBitVector getSubrangeBitVector(unsigned len, unsigned setOffset, 202 unsigned numSet); 203 204 /// Check if the pos^th variable can be expressed as a floordiv of an affine 205 /// function of other variables (where the divisor is a positive constant). 206 /// `foundRepr` contains a boolean for each variable indicating if the 207 /// explicit representation for that variable has already been computed. 208 /// Return the given array as an array of MPInts. 209 SmallVector<MPInt, 8> getMPIntVec(ArrayRef<int64_t> range); 210 /// Return the given array as an array of int64_t. 211 SmallVector<int64_t, 8> getInt64Vec(ArrayRef<MPInt> range); 212 /// Returns the `MaybeLocalRepr` struct which contains the indices of the 213 /// constraints that can be expressed as a floordiv of an affine function. If 214 /// the representation could be computed, `dividend` and `denominator` are set. 215 /// If the representation could not be computed, the kind attribute in 216 /// `MaybeLocalRepr` is set to None. 217 MaybeLocalRepr computeSingleVarRepr(const IntegerRelation &cst, 218 ArrayRef<bool> foundRepr, unsigned pos, 219 MutableArrayRef<int64_t> dividend, 220 unsigned &divisor); 221 222 /// Given two relations, A and B, add additional local vars to the sets such 223 /// that both have the union of the local vars in each set, without changing 224 /// the set of points that lie in A and B. 225 /// 226 /// While taking union, if a local var in any set has a division representation 227 /// which is a duplicate of division representation, of another local var in any 228 /// set, it is not added to the final union of local vars and is instead merged. 229 /// 230 /// On every possible merge, `merge(i, j)` is called. `i`, `j` are position 231 /// of local variables in both sets which are being merged. If `merge(i, j)` 232 /// returns true, the divisions are merged, otherwise the divisions are not 233 /// merged. 234 void mergeLocalVars(IntegerRelation &relA, IntegerRelation &relB, 235 llvm::function_ref<bool(unsigned i, unsigned j)> merge); 236 237 /// Compute the gcd of the range. 238 int64_t gcdRange(ArrayRef<int64_t> range); 239 240 /// Divide the range by its gcd and return the gcd. 241 int64_t normalizeRange(MutableArrayRef<int64_t> range); 242 243 /// Normalize the given (numerator, denominator) pair by dividing out the 244 /// common factors between them. The numerator here is an affine expression 245 /// with integer coefficients. 246 void normalizeDiv(MutableArrayRef<int64_t> num, int64_t &denom); 247 248 /// Return `coeffs` with all the elements negated. 249 SmallVector<int64_t, 8> getNegatedCoeffs(ArrayRef<int64_t> coeffs); 250 251 /// Return the complement of the given inequality. 252 /// 253 /// The complement of a_1 x_1 + ... + a_n x_ + c >= 0 is 254 /// a_1 x_1 + ... + a_n x_ + c < 0, i.e., -a_1 x_1 - ... - a_n x_ - c - 1 >= 0, 255 /// since all the variables are constrained to be integers. 256 SmallVector<int64_t, 8> getComplementIneq(ArrayRef<int64_t> ineq); 257 258 } // namespace presburger 259 } // namespace mlir 260 261 #endif // MLIR_ANALYSIS_PRESBURGER_UTILS_H 262