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