xref: /llvm-project/mlir/include/mlir/Analysis/Presburger/Utils.h (revision b7167c784486581dad3f3188232951b79c6d0fd9)
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> &dividend,
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