MLIR  21.0.0git
FlatLinearValueConstraints.cpp
Go to the documentation of this file.
1 //===- FlatLinearValueConstraints.cpp - Linear Constraint -----------------===//
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 
10 
16 #include "mlir/IR/Builders.h"
17 #include "mlir/IR/IntegerSet.h"
18 #include "mlir/Support/LLVM.h"
19 #include "llvm/ADT/STLExtras.h"
20 #include "llvm/ADT/SmallPtrSet.h"
21 #include "llvm/ADT/SmallVector.h"
22 #include "llvm/Support/Debug.h"
23 #include "llvm/Support/raw_ostream.h"
24 #include <optional>
25 
26 #define DEBUG_TYPE "flat-value-constraints"
27 
28 using namespace mlir;
29 using namespace presburger;
30 
31 //===----------------------------------------------------------------------===//
32 // AffineExprFlattener
33 //===----------------------------------------------------------------------===//
34 
35 namespace {
36 
37 // See comments for SimpleAffineExprFlattener.
38 // An AffineExprFlattenerWithLocalVars extends a SimpleAffineExprFlattener by
39 // recording constraint information associated with mod's, floordiv's, and
40 // ceildiv's in FlatLinearConstraints 'localVarCst'.
41 struct AffineExprFlattener : public SimpleAffineExprFlattener {
43 
44  // Constraints connecting newly introduced local variables (for mod's and
45  // div's) to existing (dimensional and symbolic) ones. These are always
46  // inequalities.
47  IntegerPolyhedron localVarCst;
48 
49  AffineExprFlattener(unsigned nDims, unsigned nSymbols)
50  : SimpleAffineExprFlattener(nDims, nSymbols),
51  localVarCst(PresburgerSpace::getSetSpace(nDims, nSymbols)) {};
52 
53 private:
54  // Add a local variable (needed to flatten a mod, floordiv, ceildiv expr).
55  // The local variable added is always a floordiv of a pure add/mul affine
56  // function of other variables, coefficients of which are specified in
57  // `dividend' and with respect to the positive constant `divisor'. localExpr
58  // is the simplified tree expression (AffineExpr) corresponding to the
59  // quantifier.
60  void addLocalFloorDivId(ArrayRef<int64_t> dividend, int64_t divisor,
61  AffineExpr localExpr) override {
62  SimpleAffineExprFlattener::addLocalFloorDivId(dividend, divisor, localExpr);
63  // Update localVarCst.
64  localVarCst.addLocalFloorDiv(dividend, divisor);
65  }
66 
67  LogicalResult addLocalIdSemiAffine(ArrayRef<int64_t> lhs,
69  AffineExpr localExpr) override {
70  // AffineExprFlattener does not support semi-affine expressions.
71  return failure();
72  }
73 };
74 
75 // A SemiAffineExprFlattener is an AffineExprFlattenerWithLocalVars that adds
76 // conservative bounds for semi-affine expressions (given assumptions hold). If
77 // the assumptions required to add the semi-affine bounds are found not to hold
78 // the final constraints set will be empty/inconsistent. If the assumptions are
79 // never contradicted the final bounds still only will be correct if the
80 // assumptions hold.
81 struct SemiAffineExprFlattener : public AffineExprFlattener {
82  using AffineExprFlattener::AffineExprFlattener;
83 
84  LogicalResult addLocalIdSemiAffine(ArrayRef<int64_t> lhs,
86  AffineExpr localExpr) override {
87  auto result =
89  assert(succeeded(result) &&
90  "unexpected failure in SimpleAffineExprFlattener");
91  (void)result;
92 
93  if (localExpr.getKind() == AffineExprKind::Mod) {
94  // Given two numbers a and b, division is defined as:
95  //
96  // a = bq + r
97  // 0 <= r < |b| (where |x| is the absolute value of x)
98  //
99  // q = a floordiv b
100  // r = a mod b
101 
102  // Add a new local variable (r) to represent the mod.
103  unsigned rPos = localVarCst.appendVar(VarKind::Local);
104 
105  // r >= 0 (Can ALWAYS be added)
106  localVarCst.addBound(BoundType::LB, rPos, 0);
107 
108  // r < b (Can be added if b > 0, which we assume here)
109  ArrayRef<int64_t> b = rhs;
110  SmallVector<int64_t> bSubR(b);
111  bSubR.insert(bSubR.begin() + rPos, -1);
112  // Note: bSubR = b - r
113  // So this adds the bound b - r >= 1 (equivalent to r < b)
114  localVarCst.addBound(BoundType::LB, bSubR, 1);
115 
116  // Note: The assumption of b > 0 is based on the affine expression docs,
117  // which state "RHS of mod is always a constant or a symbolic expression
118  // with a positive value." (see AffineExprKind in AffineExpr.h). If this
119  // assumption does not hold constraints (added above) are a contradiction.
120 
121  return success();
122  }
123 
124  // TODO: Support other semi-affine expressions.
125  return failure();
126  }
127 };
128 
129 } // namespace
130 
131 // Flattens the expressions in map. Returns failure if 'expr' was unable to be
132 // flattened. For example two specific cases:
133 // 1. an unhandled semi-affine expressions is found.
134 // 2. has poison expression (i.e., division by zero).
135 static LogicalResult
137  unsigned numSymbols,
138  std::vector<SmallVector<int64_t, 8>> *flattenedExprs,
139  FlatLinearConstraints *localVarCst,
140  bool addConservativeSemiAffineBounds = false) {
141  if (exprs.empty()) {
142  if (localVarCst)
143  *localVarCst = FlatLinearConstraints(numDims, numSymbols);
144  return success();
145  }
146 
147  auto flattenExprs = [&](AffineExprFlattener &flattener) -> LogicalResult {
148  // Use the same flattener to simplify each expression successively. This way
149  // local variables / expressions are shared.
150  for (auto expr : exprs) {
151  auto flattenResult = flattener.walkPostOrder(expr);
152  if (failed(flattenResult))
153  return failure();
154  }
155 
156  assert(flattener.operandExprStack.size() == exprs.size());
157  flattenedExprs->clear();
158  flattenedExprs->assign(flattener.operandExprStack.begin(),
159  flattener.operandExprStack.end());
160 
161  if (localVarCst)
162  localVarCst->clearAndCopyFrom(flattener.localVarCst);
163 
164  return success();
165  };
166 
167  if (addConservativeSemiAffineBounds) {
168  SemiAffineExprFlattener flattener(numDims, numSymbols);
169  return flattenExprs(flattener);
170  }
171 
172  AffineExprFlattener flattener(numDims, numSymbols);
173  return flattenExprs(flattener);
174 }
175 
176 // Flattens 'expr' into 'flattenedExpr'. Returns failure if 'expr' was unable to
177 // be flattened (an unhandled semi-affine was found).
179  AffineExpr expr, unsigned numDims, unsigned numSymbols,
180  SmallVectorImpl<int64_t> *flattenedExpr, FlatLinearConstraints *localVarCst,
181  bool addConservativeSemiAffineBounds) {
182  std::vector<SmallVector<int64_t, 8>> flattenedExprs;
183  LogicalResult ret =
184  ::getFlattenedAffineExprs({expr}, numDims, numSymbols, &flattenedExprs,
185  localVarCst, addConservativeSemiAffineBounds);
186  *flattenedExpr = flattenedExprs[0];
187  return ret;
188 }
189 
190 /// Flattens the expressions in map. Returns failure if 'expr' was unable to be
191 /// flattened (i.e., an unhandled semi-affine was found).
193  AffineMap map, std::vector<SmallVector<int64_t, 8>> *flattenedExprs,
194  FlatLinearConstraints *localVarCst, bool addConservativeSemiAffineBounds) {
195  if (map.getNumResults() == 0) {
196  if (localVarCst)
197  *localVarCst =
199  return success();
200  }
202  map.getResults(), map.getNumDims(), map.getNumSymbols(), flattenedExprs,
203  localVarCst, addConservativeSemiAffineBounds);
204 }
205 
207  IntegerSet set, std::vector<SmallVector<int64_t, 8>> *flattenedExprs,
208  FlatLinearConstraints *localVarCst) {
209  if (set.getNumConstraints() == 0) {
210  if (localVarCst)
211  *localVarCst =
213  return success();
214  }
216  set.getNumSymbols(), flattenedExprs,
217  localVarCst);
218 }
219 
220 //===----------------------------------------------------------------------===//
221 // FlatLinearConstraints
222 //===----------------------------------------------------------------------===//
223 
224 // Similar to `composeMap` except that no Values need be associated with the
225 // constraint system nor are they looked at -- the dimensions and symbols of
226 // `other` are expected to correspond 1:1 to `this` system.
228  assert(other.getNumDims() == getNumDimVars() && "dim mismatch");
229  assert(other.getNumSymbols() == getNumSymbolVars() && "symbol mismatch");
230 
231  std::vector<SmallVector<int64_t, 8>> flatExprs;
232  if (failed(flattenAlignedMapAndMergeLocals(other, &flatExprs)))
233  return failure();
234  assert(flatExprs.size() == other.getNumResults());
235 
236  // Add dimensions corresponding to the map's results.
237  insertDimVar(/*pos=*/0, /*num=*/other.getNumResults());
238 
239  // We add one equality for each result connecting the result dim of the map to
240  // the other variables.
241  // E.g.: if the expression is 16*i0 + i1, and this is the r^th
242  // iteration/result of the value map, we are adding the equality:
243  // d_r - 16*i0 - i1 = 0. Similarly, when flattening (i0 + 1, i0 + 8*i2), we
244  // add two equalities: d_0 - i0 - 1 == 0, d1 - i0 - 8*i2 == 0.
245  for (unsigned r = 0, e = flatExprs.size(); r < e; r++) {
246  const auto &flatExpr = flatExprs[r];
247  assert(flatExpr.size() >= other.getNumInputs() + 1);
248 
249  SmallVector<int64_t, 8> eqToAdd(getNumCols(), 0);
250  // Set the coefficient for this result to one.
251  eqToAdd[r] = 1;
252 
253  // Dims and symbols.
254  for (unsigned i = 0, f = other.getNumInputs(); i < f; i++) {
255  // Negate `eq[r]` since the newly added dimension will be set to this one.
256  eqToAdd[e + i] = -flatExpr[i];
257  }
258  // Local columns of `eq` are at the beginning.
259  unsigned j = getNumDimVars() + getNumSymbolVars();
260  unsigned end = flatExpr.size() - 1;
261  for (unsigned i = other.getNumInputs(); i < end; i++, j++) {
262  eqToAdd[j] = -flatExpr[i];
263  }
264 
265  // Constant term.
266  eqToAdd[getNumCols() - 1] = -flatExpr[flatExpr.size() - 1];
267 
268  // Add the equality connecting the result of the map to this constraint set.
269  addEquality(eqToAdd);
270  }
271 
272  return success();
273 }
274 
275 // Determine whether the variable at 'pos' (say var_r) can be expressed as
276 // modulo of another known variable (say var_n) w.r.t a constant. For example,
277 // if the following constraints hold true:
278 // ```
279 // 0 <= var_r <= divisor - 1
280 // var_n - (divisor * q_expr) = var_r
281 // ```
282 // where `var_n` is a known variable (called dividend), and `q_expr` is an
283 // `AffineExpr` (called the quotient expression), `var_r` can be written as:
284 //
285 // `var_r = var_n mod divisor`.
286 //
287 // Additionally, in a special case of the above constaints where `q_expr` is an
288 // variable itself that is not yet known (say `var_q`), it can be written as a
289 // floordiv in the following way:
290 //
291 // `var_q = var_n floordiv divisor`.
292 //
293 // First 'num' dimensional variables starting at 'offset' are
294 // derived/to-be-derived in terms of the remaining variables. The remaining
295 // variables are assigned trivial affine expressions in `memo`. For example,
296 // memo is initilized as follows for a `cst` with 5 dims, when offset=2, num=2:
297 // memo ==> d0 d1 . . d2 ...
298 // cst ==> c0 c1 c2 c3 c4 ...
299 //
300 // Returns true if the above mod or floordiv are detected, updating 'memo' with
301 // these new expressions. Returns false otherwise.
302 static bool detectAsMod(const FlatLinearConstraints &cst, unsigned pos,
303  unsigned offset, unsigned num, int64_t lbConst,
304  int64_t ubConst, MLIRContext *context,
306  assert(pos < cst.getNumVars() && "invalid position");
307 
308  // Check if a divisor satisfying the condition `0 <= var_r <= divisor - 1` can
309  // be determined.
310  if (lbConst != 0 || ubConst < 1)
311  return false;
312  int64_t divisor = ubConst + 1;
313 
314  // Check for the aforementioned conditions in each equality.
315  for (unsigned curEquality = 0, numEqualities = cst.getNumEqualities();
316  curEquality < numEqualities; curEquality++) {
317  int64_t coefficientAtPos = cst.atEq64(curEquality, pos);
318  // If current equality does not involve `var_r`, continue to the next
319  // equality.
320  if (coefficientAtPos == 0)
321  continue;
322 
323  // Constant term should be 0 in this equality.
324  if (cst.atEq64(curEquality, cst.getNumCols() - 1) != 0)
325  continue;
326 
327  // Traverse through the equality and construct the dividend expression
328  // `dividendExpr`, to contain all the variables which are known and are
329  // not divisible by `(coefficientAtPos * divisor)`. Hope here is that the
330  // `dividendExpr` gets simplified into a single variable `var_n` discussed
331  // above.
332  auto dividendExpr = getAffineConstantExpr(0, context);
333 
334  // Track the terms that go into quotient expression, later used to detect
335  // additional floordiv.
336  unsigned quotientCount = 0;
337  int quotientPosition = -1;
338  int quotientSign = 1;
339 
340  // Consider each term in the current equality.
341  unsigned curVar, e;
342  for (curVar = 0, e = cst.getNumDimAndSymbolVars(); curVar < e; ++curVar) {
343  // Ignore var_r.
344  if (curVar == pos)
345  continue;
346  int64_t coefficientOfCurVar = cst.atEq64(curEquality, curVar);
347  // Ignore vars that do not contribute to the current equality.
348  if (coefficientOfCurVar == 0)
349  continue;
350  // Check if the current var goes into the quotient expression.
351  if (coefficientOfCurVar % (divisor * coefficientAtPos) == 0) {
352  quotientCount++;
353  quotientPosition = curVar;
354  quotientSign = (coefficientOfCurVar * coefficientAtPos) > 0 ? 1 : -1;
355  continue;
356  }
357  // Variables that are part of dividendExpr should be known.
358  if (!memo[curVar])
359  break;
360  // Append the current variable to the dividend expression.
361  dividendExpr = dividendExpr + memo[curVar] * coefficientOfCurVar;
362  }
363 
364  // Can't construct expression as it depends on a yet uncomputed var.
365  if (curVar < e)
366  continue;
367 
368  // Express `var_r` in terms of the other vars collected so far.
369  if (coefficientAtPos > 0)
370  dividendExpr = (-dividendExpr).floorDiv(coefficientAtPos);
371  else
372  dividendExpr = dividendExpr.floorDiv(-coefficientAtPos);
373 
374  // Simplify the expression.
375  dividendExpr = simplifyAffineExpr(dividendExpr, cst.getNumDimVars(),
376  cst.getNumSymbolVars());
377  // Only if the final dividend expression is just a single var (which we call
378  // `var_n`), we can proceed.
379  // TODO: Handle AffineSymbolExpr as well. There is no reason to restrict it
380  // to dims themselves.
381  auto dimExpr = dyn_cast<AffineDimExpr>(dividendExpr);
382  if (!dimExpr)
383  continue;
384 
385  // Express `var_r` as `var_n % divisor` and store the expression in `memo`.
386  if (quotientCount >= 1) {
387  // Find the column corresponding to `dimExpr`. `num` columns starting at
388  // `offset` correspond to previously unknown variables. The column
389  // corresponding to the trivially known `dimExpr` can be on either side
390  // of these.
391  unsigned dimExprPos = dimExpr.getPosition();
392  unsigned dimExprCol = dimExprPos < offset ? dimExprPos : dimExprPos + num;
393  auto ub = cst.getConstantBound64(BoundType::UB, dimExprCol);
394  // If `var_n` has an upperbound that is less than the divisor, mod can be
395  // eliminated altogether.
396  if (ub && *ub < divisor)
397  memo[pos] = dimExpr;
398  else
399  memo[pos] = dimExpr % divisor;
400  // If a unique quotient `var_q` was seen, it can be expressed as
401  // `var_n floordiv divisor`.
402  if (quotientCount == 1 && !memo[quotientPosition])
403  memo[quotientPosition] = dimExpr.floorDiv(divisor) * quotientSign;
404 
405  return true;
406  }
407  }
408  return false;
409 }
410 
411 /// Check if the pos^th variable can be expressed as a floordiv of an affine
412 /// function of other variables (where the divisor is a positive constant)
413 /// given the initial set of expressions in `exprs`. If it can be, the
414 /// corresponding position in `exprs` is set as the detected affine expr. For
415 /// eg: 4q <= i + j <= 4q + 3 <=> q = (i + j) floordiv 4. An equality can
416 /// also yield a floordiv: eg. 4q = i + j <=> q = (i + j) floordiv 4. 32q + 28
417 /// <= i <= 32q + 31 => q = i floordiv 32.
418 static bool detectAsFloorDiv(const FlatLinearConstraints &cst, unsigned pos,
419  MLIRContext *context,
421  assert(pos < cst.getNumVars() && "invalid position");
422 
423  // Get upper-lower bound pair for this variable.
424  SmallVector<bool, 8> foundRepr(cst.getNumVars(), false);
425  for (unsigned i = 0, e = cst.getNumVars(); i < e; ++i)
426  if (exprs[i])
427  foundRepr[i] = true;
428 
429  SmallVector<int64_t, 8> dividend(cst.getNumCols());
430  unsigned divisor;
431  auto ulPair = computeSingleVarRepr(cst, foundRepr, pos, dividend, divisor);
432 
433  // No upper-lower bound pair found for this var.
434  if (ulPair.kind == ReprKind::None || ulPair.kind == ReprKind::Equality)
435  return false;
436 
437  // Construct the dividend expression.
438  auto dividendExpr = getAffineConstantExpr(dividend.back(), context);
439  for (unsigned c = 0, f = cst.getNumVars(); c < f; c++)
440  if (dividend[c] != 0)
441  dividendExpr = dividendExpr + dividend[c] * exprs[c];
442 
443  // Successfully detected the floordiv.
444  exprs[pos] = dividendExpr.floorDiv(divisor);
445  return true;
446 }
447 
449  bool fixedColWidth) const {
450  unsigned ncols = getNumCols();
451  bool firstNonZero = true;
452  for (unsigned j = 0; j < ncols; j++) {
453  if (j == ncols - 1) {
454  // Constant.
455  if (row[j] == 0 && !firstNonZero) {
456  if (fixedColWidth)
457  llvm::errs().indent(7);
458  } else {
459  llvm::errs() << ((row[j] >= 0) ? "+ " : "") << row[j] << ' ';
460  }
461  } else {
462  std::string var = std::string("c_") + std::to_string(j);
463  if (row[j] == 1)
464  llvm::errs() << "+ " << var << ' ';
465  else if (row[j] == -1)
466  llvm::errs() << "- " << var << ' ';
467  else if (row[j] >= 2)
468  llvm::errs() << "+ " << row[j] << '*' << var << ' ';
469  else if (row[j] <= -2)
470  llvm::errs() << "- " << -row[j] << '*' << var << ' ';
471  else if (fixedColWidth)
472  // Zero coeff.
473  llvm::errs().indent(7);
474  if (row[j] != 0)
475  firstNonZero = false;
476  }
477  }
478 }
479 
481  assert(hasConsistentState());
482  llvm::errs() << "Constraints (" << getNumDimVars() << " dims, "
483  << getNumSymbolVars() << " symbols, " << getNumLocalVars()
484  << " locals), (" << getNumConstraints() << " constraints)\n";
485  auto dumpConstraint = [&](unsigned rowPos, bool isEq) {
486  // Is it the first non-zero entry?
488  isEq ? getEquality64(rowPos) : getInequality64(rowPos);
489  dumpRow(row);
490  llvm::errs() << (isEq ? "=" : ">=") << " 0\n";
491  };
492 
493  for (unsigned i = 0, e = getNumInequalities(); i < e; i++)
494  dumpConstraint(i, /*isEq=*/false);
495  for (unsigned i = 0, e = getNumEqualities(); i < e; i++)
496  dumpConstraint(i, /*isEq=*/true);
497  llvm::errs() << '\n';
498 }
499 
500 std::pair<AffineMap, AffineMap> FlatLinearConstraints::getLowerAndUpperBound(
501  unsigned pos, unsigned offset, unsigned num, unsigned symStartPos,
502  ArrayRef<AffineExpr> localExprs, MLIRContext *context,
503  bool closedUB) const {
504  assert(pos + offset < getNumDimVars() && "invalid dim start pos");
505  assert(symStartPos >= (pos + offset) && "invalid sym start pos");
506  assert(getNumLocalVars() == localExprs.size() &&
507  "incorrect local exprs count");
508 
509  SmallVector<unsigned, 4> lbIndices, ubIndices, eqIndices;
510  getLowerAndUpperBoundIndices(pos + offset, &lbIndices, &ubIndices, &eqIndices,
511  offset, num);
512 
513  /// Add to 'b' from 'a' in set [0, offset) U [offset + num, symbStartPos).
514  auto addCoeffs = [&](ArrayRef<int64_t> a, SmallVectorImpl<int64_t> &b) {
515  b.clear();
516  for (unsigned i = 0, e = a.size(); i < e; ++i) {
517  if (i < offset || i >= offset + num)
518  b.push_back(a[i]);
519  }
520  };
521 
524  unsigned dimCount = symStartPos - num;
525  unsigned symCount = getNumDimAndSymbolVars() - symStartPos;
526  lbExprs.reserve(lbIndices.size() + eqIndices.size());
527  // Lower bound expressions.
528  for (auto idx : lbIndices) {
529  auto ineq = getInequality64(idx);
530  // Extract the lower bound (in terms of other coeff's + const), i.e., if
531  // i - j + 1 >= 0 is the constraint, 'pos' is for i the lower bound is j
532  // - 1.
533  addCoeffs(ineq, lb);
534  std::transform(lb.begin(), lb.end(), lb.begin(), std::negate<int64_t>());
535  auto expr =
536  getAffineExprFromFlatForm(lb, dimCount, symCount, localExprs, context);
537  // expr ceildiv divisor is (expr + divisor - 1) floordiv divisor
538  int64_t divisor = std::abs(ineq[pos + offset]);
539  expr = (expr + divisor - 1).floorDiv(divisor);
540  lbExprs.push_back(expr);
541  }
542 
544  ubExprs.reserve(ubIndices.size() + eqIndices.size());
545  // Upper bound expressions.
546  for (auto idx : ubIndices) {
547  auto ineq = getInequality64(idx);
548  // Extract the upper bound (in terms of other coeff's + const).
549  addCoeffs(ineq, ub);
550  auto expr =
551  getAffineExprFromFlatForm(ub, dimCount, symCount, localExprs, context);
552  expr = expr.floorDiv(std::abs(ineq[pos + offset]));
553  int64_t ubAdjustment = closedUB ? 0 : 1;
554  ubExprs.push_back(expr + ubAdjustment);
555  }
556 
557  // Equalities. It's both a lower and a upper bound.
559  for (auto idx : eqIndices) {
560  auto eq = getEquality64(idx);
561  addCoeffs(eq, b);
562  if (eq[pos + offset] > 0)
563  std::transform(b.begin(), b.end(), b.begin(), std::negate<int64_t>());
564 
565  // Extract the upper bound (in terms of other coeff's + const).
566  auto expr =
567  getAffineExprFromFlatForm(b, dimCount, symCount, localExprs, context);
568  expr = expr.floorDiv(std::abs(eq[pos + offset]));
569  // Upper bound is exclusive.
570  ubExprs.push_back(expr + 1);
571  // Lower bound.
572  expr =
573  getAffineExprFromFlatForm(b, dimCount, symCount, localExprs, context);
574  expr = expr.ceilDiv(std::abs(eq[pos + offset]));
575  lbExprs.push_back(expr);
576  }
577 
578  auto lbMap = AffineMap::get(dimCount, symCount, lbExprs, context);
579  auto ubMap = AffineMap::get(dimCount, symCount, ubExprs, context);
580 
581  return {lbMap, ubMap};
582 }
583 
584 /// Express the pos^th identifier of `cst` as an affine expression in
585 /// terms of other identifiers, if they are available in `exprs`, using the
586 /// equality at position `idx` in `cs`t. Populates `exprs` with such an
587 /// expression if possible, and return true. Returns false otherwise.
588 static bool detectAsExpr(const FlatLinearConstraints &cst, unsigned pos,
589  unsigned idx, MLIRContext *context,
591  // Initialize with a `0` expression.
592  auto expr = getAffineConstantExpr(0, context);
593 
594  // Traverse `idx`th equality and construct the possible affine expression in
595  // terms of known identifiers.
596  unsigned j, e;
597  for (j = 0, e = cst.getNumVars(); j < e; ++j) {
598  if (j == pos)
599  continue;
600  int64_t c = cst.atEq64(idx, j);
601  if (c == 0)
602  continue;
603  // If any of the involved IDs hasn't been found yet, we can't proceed.
604  if (!exprs[j])
605  break;
606  expr = expr + exprs[j] * c;
607  }
608  if (j < e)
609  // Can't construct expression as it depends on a yet uncomputed
610  // identifier.
611  return false;
612 
613  // Add constant term to AffineExpr.
614  expr = expr + cst.atEq64(idx, cst.getNumVars());
615  int64_t vPos = cst.atEq64(idx, pos);
616  assert(vPos != 0 && "expected non-zero here");
617  if (vPos > 0)
618  expr = (-expr).floorDiv(vPos);
619  else
620  // vPos < 0.
621  expr = expr.floorDiv(-vPos);
622  // Successfully constructed expression.
623  exprs[pos] = expr;
624  return true;
625 }
626 
627 /// Compute a representation of `num` identifiers starting at `offset` in `cst`
628 /// as affine expressions involving other known identifiers. Each identifier's
629 /// expression (in terms of known identifiers) is populated into `memo`.
631  MLIRContext *context, unsigned offset,
632  unsigned num,
634  // Initialize dimensional and symbolic variables.
635  for (unsigned i = 0, e = cst.getNumDimVars(); i < e; i++) {
636  if (i < offset)
637  memo[i] = getAffineDimExpr(i, context);
638  else if (i >= offset + num)
639  memo[i] = getAffineDimExpr(i - num, context);
640  }
641  for (unsigned i = cst.getNumDimVars(), e = cst.getNumDimAndSymbolVars();
642  i < e; i++)
643  memo[i] = getAffineSymbolExpr(i - cst.getNumDimVars(), context);
644 
645  bool changed;
646  do {
647  changed = false;
648  // Identify yet unknown variables as constants or mod's / floordiv's of
649  // other variables if possible.
650  for (unsigned pos = 0, f = cst.getNumVars(); pos < f; pos++) {
651  if (memo[pos])
652  continue;
653 
654  auto lbConst = cst.getConstantBound64(BoundType::LB, pos);
655  auto ubConst = cst.getConstantBound64(BoundType::UB, pos);
656  if (lbConst.has_value() && ubConst.has_value()) {
657  // Detect equality to a constant.
658  if (*lbConst == *ubConst) {
659  memo[pos] = getAffineConstantExpr(*lbConst, context);
660  changed = true;
661  continue;
662  }
663 
664  // Detect a variable as modulo of another variable w.r.t a
665  // constant.
666  if (detectAsMod(cst, pos, offset, num, *lbConst, *ubConst, context,
667  memo)) {
668  changed = true;
669  continue;
670  }
671  }
672 
673  // Detect a variable as a floordiv of an affine function of other
674  // variables (divisor is a positive constant).
675  if (detectAsFloorDiv(cst, pos, context, memo)) {
676  changed = true;
677  continue;
678  }
679 
680  // Detect a variable as an expression of other variables.
681  std::optional<unsigned> idx;
682  if (!(idx = cst.findConstraintWithNonZeroAt(pos, /*isEq=*/true)))
683  continue;
684 
685  if (detectAsExpr(cst, pos, *idx, context, memo)) {
686  changed = true;
687  continue;
688  }
689  }
690  // This loop is guaranteed to reach a fixed point - since once an
691  // variable's explicit form is computed (in memo[pos]), it's not updated
692  // again.
693  } while (changed);
694 }
695 
696 /// Computes the lower and upper bounds of the first 'num' dimensional
697 /// variables (starting at 'offset') as affine maps of the remaining
698 /// variables (dimensional and symbolic variables). Local variables are
699 /// themselves explicitly computed as affine functions of other variables in
700 /// this process if needed.
701 void FlatLinearConstraints::getSliceBounds(unsigned offset, unsigned num,
702  MLIRContext *context,
705  bool closedUB) {
706  assert(offset + num <= getNumDimVars() && "invalid range");
707 
708  // Basic simplification.
709  normalizeConstraintsByGCD();
710 
711  LLVM_DEBUG(llvm::dbgs() << "getSliceBounds for variables at positions ["
712  << offset << ", " << offset + num << ")\n");
713  LLVM_DEBUG(dumpPretty());
714 
715  // Record computed/detected variables.
716  SmallVector<AffineExpr, 8> memo(getNumVars());
717  computeUnknownVars(*this, context, offset, num, memo);
718 
719  int64_t ubAdjustment = closedUB ? 0 : 1;
720 
721  // Set the lower and upper bound maps for all the variables that were
722  // computed as affine expressions of the rest as the "detected expr" and
723  // "detected expr + 1" respectively; set the undetected ones to null.
724  std::optional<FlatLinearConstraints> tmpClone;
725  for (unsigned pos = 0; pos < num; pos++) {
726  unsigned numMapDims = getNumDimVars() - num;
727  unsigned numMapSymbols = getNumSymbolVars();
728  AffineExpr expr = memo[pos + offset];
729  if (expr)
730  expr = simplifyAffineExpr(expr, numMapDims, numMapSymbols);
731 
732  AffineMap &lbMap = (*lbMaps)[pos];
733  AffineMap &ubMap = (*ubMaps)[pos];
734 
735  if (expr) {
736  lbMap = AffineMap::get(numMapDims, numMapSymbols, expr);
737  ubMap = AffineMap::get(numMapDims, numMapSymbols, expr + ubAdjustment);
738  } else {
739  // TODO: Whenever there are local variables in the dependence
740  // constraints, we'll conservatively over-approximate, since we don't
741  // always explicitly compute them above (in the while loop).
742  if (getNumLocalVars() == 0) {
743  // Work on a copy so that we don't update this constraint system.
744  if (!tmpClone) {
745  tmpClone.emplace(FlatLinearConstraints(*this));
746  // Removing redundant inequalities is necessary so that we don't get
747  // redundant loop bounds.
748  tmpClone->removeRedundantInequalities();
749  }
750  std::tie(lbMap, ubMap) = tmpClone->getLowerAndUpperBound(
751  pos, offset, num, getNumDimVars(), /*localExprs=*/{}, context,
752  closedUB);
753  }
754 
755  // If the above fails, we'll just use the constant lower bound and the
756  // constant upper bound (if they exist) as the slice bounds.
757  // TODO: being conservative for the moment in cases that
758  // lead to multiple bounds - until getConstDifference in LoopFusion.cpp is
759  // fixed (b/126426796).
760  if (!lbMap || lbMap.getNumResults() != 1) {
761  LLVM_DEBUG(llvm::dbgs()
762  << "WARNING: Potentially over-approximating slice lb\n");
763  auto lbConst = getConstantBound64(BoundType::LB, pos + offset);
764  if (lbConst.has_value()) {
765  lbMap = AffineMap::get(numMapDims, numMapSymbols,
766  getAffineConstantExpr(*lbConst, context));
767  }
768  }
769  if (!ubMap || ubMap.getNumResults() != 1) {
770  LLVM_DEBUG(llvm::dbgs()
771  << "WARNING: Potentially over-approximating slice ub\n");
772  auto ubConst = getConstantBound64(BoundType::UB, pos + offset);
773  if (ubConst.has_value()) {
774  ubMap = AffineMap::get(
775  numMapDims, numMapSymbols,
776  getAffineConstantExpr(*ubConst + ubAdjustment, context));
777  }
778  }
779  }
780 
781  LLVM_DEBUG(llvm::dbgs() << "Slice bounds:\n");
782  LLVM_DEBUG(llvm::dbgs() << "lb map for pos = " << Twine(pos + offset)
783  << ", expr: " << lbMap << '\n');
784  LLVM_DEBUG(llvm::dbgs() << "ub map for pos = " << Twine(pos + offset)
785  << ", expr: " << ubMap << '\n');
786  }
787 }
788 
790  AffineMap map, std::vector<SmallVector<int64_t, 8>> *flattenedExprs,
791  bool addConservativeSemiAffineBounds) {
792  FlatLinearConstraints localCst;
793  if (failed(getFlattenedAffineExprs(map, flattenedExprs, &localCst,
794  addConservativeSemiAffineBounds))) {
795  LLVM_DEBUG(llvm::dbgs()
796  << "composition unimplemented for semi-affine maps\n");
797  return failure();
798  }
799 
800  // Add localCst information.
801  if (localCst.getNumLocalVars() > 0) {
802  unsigned numLocalVars = getNumLocalVars();
803  // Insert local dims of localCst at the beginning.
804  insertLocalVar(/*pos=*/0, /*num=*/localCst.getNumLocalVars());
805  // Insert local dims of `this` at the end of localCst.
806  localCst.appendLocalVar(/*num=*/numLocalVars);
807  // Dimensions of localCst and this constraint set match. Append localCst to
808  // this constraint set.
809  append(localCst);
810  }
811 
812  return success();
813 }
814 
816  BoundType type, unsigned pos, AffineMap boundMap, bool isClosedBound,
817  AddConservativeSemiAffineBounds addSemiAffineBounds) {
818  assert(boundMap.getNumDims() == getNumDimVars() && "dim mismatch");
819  assert(boundMap.getNumSymbols() == getNumSymbolVars() && "symbol mismatch");
820  assert(pos < getNumDimAndSymbolVars() && "invalid position");
821  assert((type != BoundType::EQ || isClosedBound) &&
822  "EQ bound must be closed.");
823 
824  // Equality follows the logic of lower bound except that we add an equality
825  // instead of an inequality.
826  assert((type != BoundType::EQ || boundMap.getNumResults() == 1) &&
827  "single result expected");
828  bool lower = type == BoundType::LB || type == BoundType::EQ;
829 
830  std::vector<SmallVector<int64_t, 8>> flatExprs;
831  if (failed(flattenAlignedMapAndMergeLocals(
832  boundMap, &flatExprs,
833  addSemiAffineBounds == AddConservativeSemiAffineBounds::Yes)))
834  return failure();
835  assert(flatExprs.size() == boundMap.getNumResults());
836 
837  // Add one (in)equality for each result.
838  for (const auto &flatExpr : flatExprs) {
839  SmallVector<int64_t> ineq(getNumCols(), 0);
840  // Dims and symbols.
841  for (unsigned j = 0, e = boundMap.getNumInputs(); j < e; j++) {
842  ineq[j] = lower ? -flatExpr[j] : flatExpr[j];
843  }
844  // Invalid bound: pos appears in `boundMap`.
845  // TODO: This should be an assertion. Fix `addDomainFromSliceMaps` and/or
846  // its callers to prevent invalid bounds from being added.
847  if (ineq[pos] != 0)
848  continue;
849  ineq[pos] = lower ? 1 : -1;
850  // Local columns of `ineq` are at the beginning.
851  unsigned j = getNumDimVars() + getNumSymbolVars();
852  unsigned end = flatExpr.size() - 1;
853  for (unsigned i = boundMap.getNumInputs(); i < end; i++, j++) {
854  ineq[j] = lower ? -flatExpr[i] : flatExpr[i];
855  }
856  // Make the bound closed in if flatExpr is open. The inequality is always
857  // created in the upper bound form, so the adjustment is -1.
858  int64_t boundAdjustment = (isClosedBound || type == BoundType::EQ) ? 0 : -1;
859  // Constant term.
860  ineq[getNumCols() - 1] = (lower ? -flatExpr[flatExpr.size() - 1]
861  : flatExpr[flatExpr.size() - 1]) +
862  boundAdjustment;
863  type == BoundType::EQ ? addEquality(ineq) : addInequality(ineq);
864  }
865 
866  return success();
867 }
868 
870  BoundType type, unsigned pos, AffineMap boundMap,
871  AddConservativeSemiAffineBounds addSemiAffineBounds) {
872  return addBound(type, pos, boundMap,
873  /*isClosedBound=*/type != BoundType::UB, addSemiAffineBounds);
874 }
875 
876 /// Compute an explicit representation for local vars. For all systems coming
877 /// from MLIR integer sets, maps, or expressions where local vars were
878 /// introduced to model floordivs and mods, this always succeeds.
879 LogicalResult
881  MLIRContext *context) const {
882  unsigned numDims = getNumDimVars();
883  unsigned numSyms = getNumSymbolVars();
884 
885  // Initialize dimensional and symbolic variables.
886  for (unsigned i = 0; i < numDims; i++)
887  memo[i] = getAffineDimExpr(i, context);
888  for (unsigned i = numDims, e = numDims + numSyms; i < e; i++)
889  memo[i] = getAffineSymbolExpr(i - numDims, context);
890 
891  bool changed;
892  do {
893  // Each time `changed` is true at the end of this iteration, one or more
894  // local vars would have been detected as floordivs and set in memo; so the
895  // number of null entries in memo[...] strictly reduces; so this converges.
896  changed = false;
897  for (unsigned i = 0, e = getNumLocalVars(); i < e; ++i)
898  if (!memo[numDims + numSyms + i] &&
899  detectAsFloorDiv(*this, /*pos=*/numDims + numSyms + i, context, memo))
900  changed = true;
901  } while (changed);
902 
903  ArrayRef<AffineExpr> localExprs =
904  ArrayRef<AffineExpr>(memo).take_back(getNumLocalVars());
905  return success(
906  llvm::all_of(localExprs, [](AffineExpr expr) { return expr; }));
907 }
908 
909 /// Given an equality or inequality (`isEquality` used to disambiguate) of `cst`
910 /// at `idx`, traverse and sum up `AffineExpr`s of all known ids other than the
911 /// `pos`th. Known `AffineExpr`s are given in `exprs` (unknowns are null). If
912 /// the equality/inequality contains any unknown id, return None. Otherwise
913 /// return sum as `AffineExpr`.
914 static std::optional<AffineExpr> getAsExpr(const FlatLinearConstraints &cst,
915  unsigned pos, MLIRContext *context,
916  ArrayRef<AffineExpr> exprs,
917  unsigned idx, bool isEquality) {
918  // Initialize with a `0` expression.
919  auto expr = getAffineConstantExpr(0, context);
920 
922  isEquality ? cst.getEquality64(idx) : cst.getInequality64(idx);
923 
924  // Traverse `idx`th equality and construct the possible affine expression in
925  // terms of known identifiers.
926  unsigned j, e;
927  for (j = 0, e = cst.getNumVars(); j < e; ++j) {
928  if (j == pos)
929  continue;
930  int64_t c = row[j];
931  if (c == 0)
932  continue;
933  // If any of the involved IDs hasn't been found yet, we can't proceed.
934  if (!exprs[j])
935  break;
936  expr = expr + exprs[j] * c;
937  }
938  if (j < e)
939  // Can't construct expression as it depends on a yet uncomputed
940  // identifier.
941  return std::nullopt;
942 
943  // Add constant term to AffineExpr.
944  expr = expr + row[cst.getNumVars()];
945  return expr;
946 }
947 
949  MLIRContext *context, unsigned pos, AffineMap *lb, AffineMap *ub,
950  unsigned *minLbPos, unsigned *minUbPos) const {
951 
952  assert(pos < getNumDimVars() && "Invalid identifier position");
953 
954  auto freeOfUnknownLocalVars = [&](ArrayRef<int64_t> cst,
955  ArrayRef<AffineExpr> whiteListCols) {
956  for (int i = getNumDimAndSymbolVars(), e = cst.size() - 1; i < e; ++i) {
957  if (whiteListCols[i] && whiteListCols[i].isSymbolicOrConstant())
958  continue;
959  if (cst[i] != 0)
960  return false;
961  }
962  return true;
963  };
964 
965  // Detect the necesary local variables first.
966  SmallVector<AffineExpr, 8> memo(getNumVars(), AffineExpr());
967  (void)computeLocalVars(memo, context);
968 
969  // Find an equality for 'pos'^th identifier that equates it to some function
970  // of the symbolic identifiers (+ constant).
971  int eqPos = findEqualityToConstant(pos, /*symbolic=*/true);
972  // If the equality involves a local var that can not be expressed as a
973  // symbolic or constant affine expression, we bail out.
974  if (eqPos != -1 && freeOfUnknownLocalVars(getEquality64(eqPos), memo)) {
975  // This identifier can only take a single value.
976  if (lb && detectAsExpr(*this, pos, eqPos, context, memo)) {
977  AffineExpr equalityExpr =
978  simplifyAffineExpr(memo[pos], 0, getNumSymbolVars());
979  *lb = AffineMap::get(/*dimCount=*/0, getNumSymbolVars(), equalityExpr);
980  if (ub)
981  *ub = *lb;
982  }
983  if (minLbPos)
984  *minLbPos = eqPos;
985  if (minUbPos)
986  *minUbPos = eqPos;
987  return 1;
988  }
989 
990  // Positions of constraints that are lower/upper bounds on the variable.
991  SmallVector<unsigned, 4> lbIndices, ubIndices;
992 
993  // Note inequalities that give lower and upper bounds.
994  getLowerAndUpperBoundIndices(pos, &lbIndices, &ubIndices,
995  /*eqIndices=*/nullptr, /*offset=*/0,
996  /*num=*/getNumDimVars());
997 
998  std::optional<int64_t> minDiff = std::nullopt;
999  unsigned minLbPosition = 0, minUbPosition = 0;
1000  AffineExpr minLbExpr, minUbExpr;
1001 
1002  // Traverse each lower bound and upper bound pair, to compute the difference
1003  // between them.
1004  for (unsigned ubPos : ubIndices) {
1005  // Construct sum of all ids other than `pos`th in the given upper bound row.
1006  std::optional<AffineExpr> maybeUbExpr =
1007  getAsExpr(*this, pos, context, memo, ubPos, /*isEquality=*/false);
1008  if (!maybeUbExpr.has_value() || !(*maybeUbExpr).isSymbolicOrConstant())
1009  continue;
1010 
1011  // Canonical form of an inequality that constrains the upper bound on
1012  // an id `x_i` is of the form:
1013  // `c_1*x_1 + c_2*x_2 + ... + c_0 >= 0`, where `c_i` <= -1.
1014  // Therefore the upper bound on `x_i` will be
1015  // `(
1016  // sum(c_j*x_j) where j != i
1017  // +
1018  // c_0
1019  // )
1020  // /
1021  // -(c_i)`. Divison here is a floorDiv.
1022  AffineExpr ubExpr = maybeUbExpr->floorDiv(-atIneq64(ubPos, pos));
1023  assert(-atIneq64(ubPos, pos) > 0 && "invalid upper bound index");
1024 
1025  // Go over each lower bound.
1026  for (unsigned lbPos : lbIndices) {
1027  // Construct sum of all ids other than `pos`th in the given lower bound
1028  // row.
1029  std::optional<AffineExpr> maybeLbExpr =
1030  getAsExpr(*this, pos, context, memo, lbPos, /*isEquality=*/false);
1031  if (!maybeLbExpr.has_value() || !(*maybeLbExpr).isSymbolicOrConstant())
1032  continue;
1033 
1034  // Canonical form of an inequality that is constraining the lower bound
1035  // on an id `x_i is of the form:
1036  // `c_1*x_1 + c_2*x_2 + ... + c_0 >= 0`, where `c_i` >= 1.
1037  // Therefore upperBound on `x_i` will be
1038  // `-(
1039  // sum(c_j*x_j) where j != i
1040  // +
1041  // c_0
1042  // )
1043  // /
1044  // c_i`. Divison here is a ceilDiv.
1045  int64_t divisor = atIneq64(lbPos, pos);
1046  // We convert the `ceilDiv` for floordiv with the formula:
1047  // `expr ceildiv divisor is (expr + divisor - 1) floordiv divisor`,
1048  // since uniformly keeping divisons as `floorDiv` helps their
1049  // simplification.
1050  AffineExpr lbExpr = (-(*maybeLbExpr) + divisor - 1).floorDiv(divisor);
1051  assert(atIneq64(lbPos, pos) > 0 && "invalid lower bound index");
1052 
1053  AffineExpr difference =
1054  simplifyAffineExpr(ubExpr - lbExpr + 1, 0, getNumSymbolVars());
1055  // If the difference is not constant, ignore the lower bound - upper bound
1056  // pair.
1057  auto constantDiff = dyn_cast<AffineConstantExpr>(difference);
1058  if (!constantDiff)
1059  continue;
1060 
1061  int64_t diffValue = constantDiff.getValue();
1062  // This bound is non-negative by definition.
1063  diffValue = std::max<int64_t>(diffValue, 0);
1064  if (!minDiff || diffValue < *minDiff) {
1065  minDiff = diffValue;
1066  minLbPosition = lbPos;
1067  minUbPosition = ubPos;
1068  minLbExpr = lbExpr;
1069  minUbExpr = ubExpr;
1070  }
1071  }
1072  }
1073 
1074  // Populate outputs where available and needed.
1075  if (lb && minDiff) {
1076  *lb = AffineMap::get(/*dimCount=*/0, getNumSymbolVars(), minLbExpr);
1077  }
1078  if (ub)
1079  *ub = AffineMap::get(/*dimCount=*/0, getNumSymbolVars(), minUbExpr);
1080  if (minLbPos)
1081  *minLbPos = minLbPosition;
1082  if (minUbPos)
1083  *minUbPos = minUbPosition;
1084 
1085  return minDiff;
1086 }
1087 
1089  if (getNumConstraints() == 0)
1090  // Return universal set (always true): 0 == 0.
1091  return IntegerSet::get(getNumDimVars(), getNumSymbolVars(),
1092  getAffineConstantExpr(/*constant=*/0, context),
1093  /*eqFlags=*/true);
1094 
1095  // Construct local references.
1096  SmallVector<AffineExpr, 8> memo(getNumVars(), AffineExpr());
1097 
1098  if (failed(computeLocalVars(memo, context))) {
1099  // Check if the local variables without an explicit representation have
1100  // zero coefficients everywhere.
1101  SmallVector<unsigned> noLocalRepVars;
1102  unsigned numDimsSymbols = getNumDimAndSymbolVars();
1103  for (unsigned i = numDimsSymbols, e = getNumVars(); i < e; ++i) {
1104  if (!memo[i] && !isColZero(/*pos=*/i))
1105  noLocalRepVars.push_back(i - numDimsSymbols);
1106  }
1107  if (!noLocalRepVars.empty()) {
1108  LLVM_DEBUG({
1109  llvm::dbgs() << "local variables at position(s) ";
1110  llvm::interleaveComma(noLocalRepVars, llvm::dbgs());
1111  llvm::dbgs() << " do not have an explicit representation in:\n";
1112  this->dump();
1113  });
1114  return IntegerSet();
1115  }
1116  }
1117 
1118  ArrayRef<AffineExpr> localExprs =
1119  ArrayRef<AffineExpr>(memo).take_back(getNumLocalVars());
1120 
1121  // Construct the IntegerSet from the equalities/inequalities.
1122  unsigned numDims = getNumDimVars();
1123  unsigned numSyms = getNumSymbolVars();
1124 
1125  SmallVector<bool, 16> eqFlags(getNumConstraints());
1126  std::fill(eqFlags.begin(), eqFlags.begin() + getNumEqualities(), true);
1127  std::fill(eqFlags.begin() + getNumEqualities(), eqFlags.end(), false);
1128 
1130  exprs.reserve(getNumConstraints());
1131 
1132  for (unsigned i = 0, e = getNumEqualities(); i < e; ++i)
1133  exprs.push_back(getAffineExprFromFlatForm(getEquality64(i), numDims,
1134  numSyms, localExprs, context));
1135  for (unsigned i = 0, e = getNumInequalities(); i < e; ++i)
1136  exprs.push_back(getAffineExprFromFlatForm(getInequality64(i), numDims,
1137  numSyms, localExprs, context));
1138  return IntegerSet::get(numDims, numSyms, exprs, eqFlags);
1139 }
1140 
1141 //===----------------------------------------------------------------------===//
1142 // FlatLinearValueConstraints
1143 //===----------------------------------------------------------------------===//
1144 
1145 // Construct from an IntegerSet.
1147  ValueRange operands)
1148  : FlatLinearConstraints(set.getNumInequalities(), set.getNumEqualities(),
1149  set.getNumDims() + set.getNumSymbols() + 1,
1150  set.getNumDims(), set.getNumSymbols(),
1151  /*numLocals=*/0) {
1152  assert((operands.empty() || set.getNumInputs() == operands.size()) &&
1153  "operand count mismatch");
1154  // Set the values for the non-local variables.
1155  for (unsigned i = 0, e = operands.size(); i < e; ++i)
1156  setValue(i, operands[i]);
1157 
1158  // Flatten expressions and add them to the constraint system.
1159  std::vector<SmallVector<int64_t, 8>> flatExprs;
1160  FlatLinearConstraints localVarCst;
1161  if (failed(getFlattenedAffineExprs(set, &flatExprs, &localVarCst))) {
1162  assert(false && "flattening unimplemented for semi-affine integer sets");
1163  return;
1164  }
1165  assert(flatExprs.size() == set.getNumConstraints());
1166  insertVar(VarKind::Local, getNumVarKind(VarKind::Local),
1167  /*num=*/localVarCst.getNumLocalVars());
1168 
1169  for (unsigned i = 0, e = flatExprs.size(); i < e; ++i) {
1170  const auto &flatExpr = flatExprs[i];
1171  assert(flatExpr.size() == getNumCols());
1172  if (set.getEqFlags()[i]) {
1173  addEquality(flatExpr);
1174  } else {
1175  addInequality(flatExpr);
1176  }
1177  }
1178  // Add the other constraints involving local vars from flattening.
1179  append(localVarCst);
1180 }
1181 
1183  unsigned pos = getNumDimVars();
1184  return insertVar(VarKind::SetDim, pos, vals);
1185 }
1186 
1188  unsigned pos = getNumSymbolVars();
1189  return insertVar(VarKind::Symbol, pos, vals);
1190 }
1191 
1193  ValueRange vals) {
1194  return insertVar(VarKind::SetDim, pos, vals);
1195 }
1196 
1198  ValueRange vals) {
1199  return insertVar(VarKind::Symbol, pos, vals);
1200 }
1201 
1203  unsigned num) {
1204  unsigned absolutePos = IntegerPolyhedron::insertVar(kind, pos, num);
1205 
1206  return absolutePos;
1207 }
1208 
1210  ValueRange vals) {
1211  assert(!vals.empty() && "expected ValueRange with Values.");
1212  assert(kind != VarKind::Local &&
1213  "values cannot be attached to local variables.");
1214  unsigned num = vals.size();
1215  unsigned absolutePos = IntegerPolyhedron::insertVar(kind, pos, num);
1216 
1217  // If a Value is provided, insert it; otherwise use std::nullopt.
1218  for (unsigned i = 0, e = vals.size(); i < e; ++i)
1219  if (vals[i])
1220  setValue(absolutePos + i, vals[i]);
1221 
1222  return absolutePos;
1223 }
1224 
1225 /// Checks if two constraint systems are in the same space, i.e., if they are
1226 /// associated with the same set of variables, appearing in the same order.
1228  const FlatLinearValueConstraints &b) {
1229  if (a.getNumDomainVars() != b.getNumDomainVars() ||
1230  a.getNumRangeVars() != b.getNumRangeVars() ||
1232  return false;
1234  bMaybeValues = b.getMaybeValues();
1235  return std::equal(aMaybeValues.begin(), aMaybeValues.end(),
1236  bMaybeValues.begin(), bMaybeValues.end());
1237 }
1238 
1239 /// Calls areVarsAligned to check if two constraint systems have the same set
1240 /// of variables in the same order.
1242  const FlatLinearConstraints &other) {
1243  return areVarsAligned(*this, other);
1244 }
1245 
1246 /// Checks if the SSA values associated with `cst`'s variables in range
1247 /// [start, end) are unique.
1248 static bool LLVM_ATTRIBUTE_UNUSED areVarsUnique(
1249  const FlatLinearValueConstraints &cst, unsigned start, unsigned end) {
1250 
1251  assert(start <= cst.getNumDimAndSymbolVars() &&
1252  "Start position out of bounds");
1253  assert(end <= cst.getNumDimAndSymbolVars() && "End position out of bounds");
1254 
1255  if (start >= end)
1256  return true;
1257 
1258  SmallPtrSet<Value, 8> uniqueVars;
1259  SmallVector<std::optional<Value>, 8> maybeValuesAll = cst.getMaybeValues();
1260  ArrayRef<std::optional<Value>> maybeValues = {maybeValuesAll.data() + start,
1261  maybeValuesAll.data() + end};
1262 
1263  for (std::optional<Value> val : maybeValues)
1264  if (val && !uniqueVars.insert(*val).second)
1265  return false;
1266 
1267  return true;
1268 }
1269 
1270 /// Checks if the SSA values associated with `cst`'s variables are unique.
1271 static bool LLVM_ATTRIBUTE_UNUSED
1273  return areVarsUnique(cst, 0, cst.getNumDimAndSymbolVars());
1274 }
1275 
1276 /// Checks if the SSA values associated with `cst`'s variables of kind `kind`
1277 /// are unique.
1278 static bool LLVM_ATTRIBUTE_UNUSED
1280 
1281  if (kind == VarKind::SetDim)
1282  return areVarsUnique(cst, 0, cst.getNumDimVars());
1283  if (kind == VarKind::Symbol)
1284  return areVarsUnique(cst, cst.getNumDimVars(),
1285  cst.getNumDimAndSymbolVars());
1286  llvm_unreachable("Unexpected VarKind");
1287 }
1288 
1289 /// Merge and align the variables of A and B starting at 'offset', so that
1290 /// both constraint systems get the union of the contained variables that is
1291 /// dimension-wise and symbol-wise unique; both constraint systems are updated
1292 /// so that they have the union of all variables, with A's original
1293 /// variables appearing first followed by any of B's variables that didn't
1294 /// appear in A. Local variables in B that have the same division
1295 /// representation as local variables in A are merged into one. We allow A
1296 /// and B to have non-unique values for their variables; in such cases, they are
1297 /// still aligned with the variables appearing first aligned with those
1298 /// appearing first in the other system from left to right.
1299 // E.g.: Input: A has ((%i, %j) [%M, %N]) and B has (%k, %j) [%P, %N, %M])
1300 // Output: both A, B have (%i, %j, %k) [%M, %N, %P]
1301 static void mergeAndAlignVars(unsigned offset, FlatLinearValueConstraints *a,
1303  assert(offset <= a->getNumDimVars() && offset <= b->getNumDimVars());
1304 
1305  assert(llvm::all_of(
1306  llvm::drop_begin(a->getMaybeValues(), offset),
1307  [](const std::optional<Value> &var) { return var.has_value(); }));
1308 
1309  assert(llvm::all_of(
1310  llvm::drop_begin(b->getMaybeValues(), offset),
1311  [](const std::optional<Value> &var) { return var.has_value(); }));
1312 
1313  SmallVector<Value, 4> aDimValues;
1314  a->getValues(offset, a->getNumDimVars(), &aDimValues);
1315 
1316  {
1317  // Merge dims from A into B.
1318  unsigned d = offset;
1319  for (Value aDimValue : aDimValues) {
1320  unsigned loc;
1321  // Find from the position `d` since we'd like to also consider the
1322  // possibility of multiple variables with the same `Value`. We align with
1323  // the next appearing one.
1324  if (b->findVar(aDimValue, &loc, d)) {
1325  assert(loc >= offset && "A's dim appears in B's aligned range");
1326  assert(loc < b->getNumDimVars() &&
1327  "A's dim appears in B's non-dim position");
1328  b->swapVar(d, loc);
1329  } else {
1330  b->insertDimVar(d, aDimValue);
1331  }
1332  d++;
1333  }
1334  // Dimensions that are in B, but not in A, are added at the end.
1335  for (unsigned t = a->getNumDimVars(), e = b->getNumDimVars(); t < e; t++) {
1336  a->appendDimVar(b->getValue(t));
1337  }
1338  assert(a->getNumDimVars() == b->getNumDimVars() &&
1339  "expected same number of dims");
1340  }
1341 
1342  // Merge and align symbols of A and B
1343  a->mergeSymbolVars(*b);
1344  // Merge and align locals of A and B
1345  a->mergeLocalVars(*b);
1346 
1347  assert(areVarsAligned(*a, *b) && "IDs expected to be aligned");
1348 }
1349 
1350 // Call 'mergeAndAlignVars' to align constraint systems of 'this' and 'other'.
1352  unsigned offset, FlatLinearValueConstraints *other) {
1353  mergeAndAlignVars(offset, this, other);
1354 }
1355 
1356 /// Merge and align symbols of `this` and `other` such that both get union of
1357 /// of symbols. Existing symbols need not be unique; they will be aligned from
1358 /// left to right with duplicates aligned in the same order. Symbols with Value
1359 /// as `None` are considered to be inequal to all other symbols.
1361  FlatLinearValueConstraints &other) {
1362 
1363  SmallVector<Value, 4> aSymValues;
1364  getValues(getNumDimVars(), getNumDimAndSymbolVars(), &aSymValues);
1365 
1366  // Merge symbols: merge symbols into `other` first from `this`.
1367  unsigned s = other.getNumDimVars();
1368  for (Value aSymValue : aSymValues) {
1369  unsigned loc;
1370  // If the var is a symbol in `other`, then align it, otherwise assume that
1371  // it is a new symbol. Search in `other` starting at position `s` since the
1372  // left of it is aligned.
1373  if (other.findVar(aSymValue, &loc, s) && loc >= other.getNumDimVars() &&
1374  loc < other.getNumDimAndSymbolVars())
1375  other.swapVar(s, loc);
1376  else
1377  other.insertSymbolVar(s - other.getNumDimVars(), aSymValue);
1378  s++;
1379  }
1380 
1381  // Symbols that are in other, but not in this, are added at the end.
1382  for (unsigned t = other.getNumDimVars() + getNumSymbolVars(),
1383  e = other.getNumDimAndSymbolVars();
1384  t < e; t++)
1386 
1387  assert(getNumSymbolVars() == other.getNumSymbolVars() &&
1388  "expected same number of symbols");
1389 }
1390 
1392  unsigned varLimit) {
1393  IntegerPolyhedron::removeVarRange(kind, varStart, varLimit);
1394 }
1395 
1396 AffineMap
1398  ValueRange operands) const {
1399  assert(map.getNumInputs() == operands.size() && "number of inputs mismatch");
1400 
1401  SmallVector<Value> dims, syms;
1402 #ifndef NDEBUG
1403  SmallVector<Value> newSyms;
1404  SmallVector<Value> *newSymsPtr = &newSyms;
1405 #else
1406  SmallVector<Value> *newSymsPtr = nullptr;
1407 #endif // NDEBUG
1408 
1409  dims.reserve(getNumDimVars());
1410  syms.reserve(getNumSymbolVars());
1411  for (unsigned i = 0, e = getNumVarKind(VarKind::SetDim); i < e; ++i) {
1412  Identifier id = space.getId(VarKind::SetDim, i);
1413  dims.push_back(id.hasValue() ? Value(id.getValue<Value>()) : Value());
1414  }
1415  for (unsigned i = 0, e = getNumVarKind(VarKind::Symbol); i < e; ++i) {
1416  Identifier id = space.getId(VarKind::Symbol, i);
1417  syms.push_back(id.hasValue() ? Value(id.getValue<Value>()) : Value());
1418  }
1419 
1420  AffineMap alignedMap =
1421  alignAffineMapWithValues(map, operands, dims, syms, newSymsPtr);
1422  // All symbols are already part of this FlatAffineValueConstraints.
1423  assert(syms.size() == newSymsPtr->size() && "unexpected new/missing symbols");
1424  assert(std::equal(syms.begin(), syms.end(), newSymsPtr->begin()) &&
1425  "unexpected new/missing symbols");
1426  return alignedMap;
1427 }
1428 
1430  unsigned offset) const {
1432  for (unsigned i = offset, e = maybeValues.size(); i < e; ++i)
1433  if (maybeValues[i] && maybeValues[i].value() == val) {
1434  *pos = i;
1435  return true;
1436  }
1437  return false;
1438 }
1439 
1441  unsigned pos;
1442  return findVar(val, &pos, 0);
1443 }
1444 
1446  int64_t value) {
1447  unsigned pos;
1448  if (!findVar(val, &pos))
1449  // This is a pre-condition for this method.
1450  assert(0 && "var not found");
1451  addBound(type, pos, value);
1452 }
1453 
1454 void FlatLinearConstraints::printSpace(raw_ostream &os) const {
1456  os << "(";
1457  for (unsigned i = 0, e = getNumDimAndSymbolVars(); i < e; i++)
1458  os << "None\t";
1459  for (unsigned i = getVarKindOffset(VarKind::Local),
1460  e = getVarKindEnd(VarKind::Local);
1461  i < e; ++i)
1462  os << "Local\t";
1463  os << "const)\n";
1464 }
1465 
1466 void FlatLinearValueConstraints::printSpace(raw_ostream &os) const {
1468  os << "(";
1469  for (unsigned i = 0, e = getNumDimAndSymbolVars(); i < e; i++) {
1470  if (hasValue(i))
1471  os << "Value\t";
1472  else
1473  os << "None\t";
1474  }
1475  for (unsigned i = getVarKindOffset(VarKind::Local),
1476  e = getVarKindEnd(VarKind::Local);
1477  i < e; ++i)
1478  os << "Local\t";
1479  os << "const)\n";
1480 }
1481 
1483  unsigned pos;
1484  bool ret = findVar(val, &pos);
1485  assert(ret);
1486  (void)ret;
1488 }
1489 
1491  const FlatLinearValueConstraints &otherCst) {
1492  assert(otherCst.getNumDimVars() == getNumDimVars() && "dims mismatch");
1494  otherMaybeValues =
1495  otherCst.getMaybeValues();
1496  assert(std::equal(maybeValues.begin(), maybeValues.begin() + getNumDimVars(),
1497  otherMaybeValues.begin(),
1498  otherMaybeValues.begin() + getNumDimVars()) &&
1499  "dim values mismatch");
1500  assert(otherCst.getNumLocalVars() == 0 && "local vars not supported here");
1501  assert(getNumLocalVars() == 0 && "local vars not supported yet here");
1502 
1503  // Align `other` to this.
1504  if (!areVarsAligned(*this, otherCst)) {
1505  FlatLinearValueConstraints otherCopy(otherCst);
1506  mergeAndAlignVars(/*offset=*/getNumDimVars(), this, &otherCopy);
1507  return IntegerPolyhedron::unionBoundingBox(otherCopy);
1508  }
1509 
1510  return IntegerPolyhedron::unionBoundingBox(otherCst);
1511 }
1512 
1513 //===----------------------------------------------------------------------===//
1514 // Helper functions
1515 //===----------------------------------------------------------------------===//
1516 
1518  ValueRange dims, ValueRange syms,
1519  SmallVector<Value> *newSyms) {
1520  assert(operands.size() == map.getNumInputs() &&
1521  "expected same number of operands and map inputs");
1522  MLIRContext *ctx = map.getContext();
1523  Builder builder(ctx);
1524  SmallVector<AffineExpr> dimReplacements(map.getNumDims(), {});
1525  unsigned numSymbols = syms.size();
1526  SmallVector<AffineExpr> symReplacements(map.getNumSymbols(), {});
1527  if (newSyms) {
1528  newSyms->clear();
1529  newSyms->append(syms.begin(), syms.end());
1530  }
1531 
1532  for (const auto &operand : llvm::enumerate(operands)) {
1533  // Compute replacement dim/sym of operand.
1534  AffineExpr replacement;
1535  auto dimIt = llvm::find(dims, operand.value());
1536  auto symIt = llvm::find(syms, operand.value());
1537  if (dimIt != dims.end()) {
1538  replacement =
1539  builder.getAffineDimExpr(std::distance(dims.begin(), dimIt));
1540  } else if (symIt != syms.end()) {
1541  replacement =
1542  builder.getAffineSymbolExpr(std::distance(syms.begin(), symIt));
1543  } else {
1544  // This operand is neither a dimension nor a symbol. Add it as a new
1545  // symbol.
1546  replacement = builder.getAffineSymbolExpr(numSymbols++);
1547  if (newSyms)
1548  newSyms->push_back(operand.value());
1549  }
1550  // Add to corresponding replacements vector.
1551  if (operand.index() < map.getNumDims()) {
1552  dimReplacements[operand.index()] = replacement;
1553  } else {
1554  symReplacements[operand.index() - map.getNumDims()] = replacement;
1555  }
1556  }
1557 
1558  return map.replaceDimsAndSymbols(dimReplacements, symReplacements,
1559  dims.size(), numSymbols);
1560 }
1561 
1562 LogicalResult
1564  MultiAffineFunction &multiAff) {
1566  std::vector<SmallVector<int64_t, 8>> flattenedExprs;
1567  LogicalResult result = getFlattenedAffineExprs(map, &flattenedExprs, &cst);
1568 
1569  if (result.failed())
1570  return failure();
1571 
1572  DivisionRepr divs = cst.getLocalReprs();
1573  assert(divs.hasAllReprs() &&
1574  "AffineMap cannot produce divs without local representation");
1575 
1576  // TODO: We shouldn't have to do this conversion.
1578  map.getNumInputs() + divs.getNumDivs() + 1);
1579  for (unsigned i = 0, e = flattenedExprs.size(); i < e; ++i)
1580  for (unsigned j = 0, f = flattenedExprs[i].size(); j < f; ++j)
1581  mat(i, j) = flattenedExprs[i][j];
1582 
1583  multiAff = MultiAffineFunction(
1584  PresburgerSpace::getRelationSpace(map.getNumDims(), map.getNumResults(),
1585  map.getNumSymbols(), divs.getNumDivs()),
1586  mat, divs);
1587 
1588  return success();
1589 }
static bool detectAsFloorDiv(const FlatLinearConstraints &cst, unsigned pos, MLIRContext *context, SmallVectorImpl< AffineExpr > &exprs)
Check if the pos^th variable can be expressed as a floordiv of an affine function of other variables ...
static bool detectAsMod(const FlatLinearConstraints &cst, unsigned pos, unsigned offset, unsigned num, int64_t lbConst, int64_t ubConst, MLIRContext *context, SmallVectorImpl< AffineExpr > &memo)
static bool LLVM_ATTRIBUTE_UNUSED areVarsUnique(const FlatLinearValueConstraints &cst, unsigned start, unsigned end)
Checks if the SSA values associated with cst's variables in range [start, end) are unique.
static void computeUnknownVars(const FlatLinearConstraints &cst, MLIRContext *context, unsigned offset, unsigned num, SmallVectorImpl< AffineExpr > &memo)
Compute a representation of num identifiers starting at offset in cst as affine expressions involving...
static LogicalResult getFlattenedAffineExprs(ArrayRef< AffineExpr > exprs, unsigned numDims, unsigned numSymbols, std::vector< SmallVector< int64_t, 8 >> *flattenedExprs, FlatLinearConstraints *localVarCst, bool addConservativeSemiAffineBounds=false)
static bool areVarsAligned(const FlatLinearValueConstraints &a, const FlatLinearValueConstraints &b)
Checks if two constraint systems are in the same space, i.e., if they are associated with the same se...
static bool detectAsExpr(const FlatLinearConstraints &cst, unsigned pos, unsigned idx, MLIRContext *context, SmallVectorImpl< AffineExpr > &exprs)
Express the pos^th identifier of cst as an affine expression in terms of other identifiers,...
static void mergeAndAlignVars(unsigned offset, FlatLinearValueConstraints *a, FlatLinearValueConstraints *b)
Merge and align the variables of A and B starting at 'offset', so that both constraint systems get th...
static std::optional< AffineExpr > getAsExpr(const FlatLinearConstraints &cst, unsigned pos, MLIRContext *context, ArrayRef< AffineExpr > exprs, unsigned idx, bool isEquality)
Given an equality or inequality (isEquality used to disambiguate) of cst at idx, traverse and sum up ...
union mlir::linalg::@1183::ArityGroupAndKind::Kind kind
Base type for affine expression.
Definition: AffineExpr.h:68
AffineExpr floorDiv(uint64_t v) const
Definition: AffineExpr.cpp:921
AffineExprKind getKind() const
Return the classification for this type.
Definition: AffineExpr.cpp:35
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
Definition: AffineMap.h:46
MLIRContext * getContext() const
Definition: AffineMap.cpp:343
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
unsigned getNumSymbols() const
Definition: AffineMap.cpp:398
unsigned getNumDims() const
Definition: AffineMap.cpp:394
ArrayRef< AffineExpr > getResults() const
Definition: AffineMap.cpp:407
unsigned getNumResults() const
Definition: AffineMap.cpp:402
AffineMap replaceDimsAndSymbols(ArrayRef< AffineExpr > dimReplacements, ArrayRef< AffineExpr > symReplacements, unsigned numResultDims, unsigned numResultSyms) const
This method substitutes any uses of dimensions and symbols (e.g.
Definition: AffineMap.cpp:500
unsigned getNumInputs() const
Definition: AffineMap.cpp:403
This class is a general helper class for creating context-global objects like types,...
Definition: Builders.h:51
AffineExpr getAffineSymbolExpr(unsigned position)
Definition: Builders.cpp:364
AffineExpr getAffineDimExpr(unsigned position)
Definition: Builders.cpp:360
FlatLinearConstraints is an extension of IntegerPolyhedron.
IntegerSet getAsIntegerSet(MLIRContext *context) const
Returns the constraint system as an integer set.
LogicalResult flattenAlignedMapAndMergeLocals(AffineMap map, std::vector< SmallVector< int64_t, 8 >> *flattenedExprs, bool addConservativeSemiAffineBounds=false)
Given an affine map that is aligned with this constraint system:
unsigned appendLocalVar(unsigned num=1)
void printSpace(raw_ostream &os) const override
Prints the number of constraints, dimensions, symbols and locals in the FlatLinearConstraints.
void dumpRow(ArrayRef< int64_t > row, bool fixedColWidth=true) const
An easier to read dump of a row of the same width as the number of columns.
void dumpPretty() const
A more human-readable version of dump().
AddConservativeSemiAffineBounds
Flag to control if conservative semi-affine bounds should be added in addBound().
LogicalResult composeMatchingMap(AffineMap other)
Composes an affine map whose dimensions and symbols match one to one with the dimensions and symbols ...
void getSliceBounds(unsigned offset, unsigned num, MLIRContext *context, SmallVectorImpl< AffineMap > *lbMaps, SmallVectorImpl< AffineMap > *ubMaps, bool closedUB=false)
Computes the lower and upper bounds of the first num dimensional variables (starting at offset) as an...
LogicalResult addBound(presburger::BoundType type, unsigned pos, AffineMap boundMap, bool isClosedBound, AddConservativeSemiAffineBounds=AddConservativeSemiAffineBounds::No)
Adds a bound for the variable at the specified position with constraints being drawn from the specifi...
std::pair< AffineMap, AffineMap > getLowerAndUpperBound(unsigned pos, unsigned offset, unsigned num, unsigned symStartPos, ArrayRef< AffineExpr > localExprs, MLIRContext *context, bool closedUB=false) const
Gets the lower and upper bound of the offset + posth variable treating [0, offset) U [offset + num,...
LogicalResult computeLocalVars(SmallVectorImpl< AffineExpr > &memo, MLIRContext *context) const
Compute an explicit representation for local vars.
std::optional< int64_t > getConstantBoundOnDimSize(MLIRContext *context, unsigned pos, AffineMap *lb=nullptr, AffineMap *ub=nullptr, unsigned *minLbPos=nullptr, unsigned *minUbPos=nullptr) const
Returns a non-negative constant bound on the extent (upper bound - lower bound) of the specified vari...
FlatLinearValueConstraints represents an extension of FlatLinearConstraints where each non-local vari...
unsigned appendDimVar(unsigned num=1)
Append variables of the specified kind after the last variable of that kind.
unsigned insertDimVar(unsigned pos, ValueRange vals)
LogicalResult unionBoundingBox(const FlatLinearValueConstraints &other)
Updates the constraints to be the smallest bounding (enclosing) box that contains the points of this ...
void mergeAndAlignVarsWithOther(unsigned offset, FlatLinearValueConstraints *other)
Merge and align the variables of this and other starting at offset, so that both constraint systems g...
SmallVector< std::optional< Value > > getMaybeValues() const
unsigned insertSymbolVar(unsigned pos, unsigned num=1)
bool hasValue(unsigned pos) const
Returns true if the pos^th variable has an associated Value.
Value getValue(unsigned pos) const
Returns the Value associated with the pos^th variable.
void printSpace(raw_ostream &os) const override
Prints the number of constraints, dimensions, symbols and locals in the FlatAffineValueConstraints.
void mergeSymbolVars(FlatLinearValueConstraints &other)
Merge and align symbols of this and other such that both get union of of symbols that are unique.
void projectOut(Value val)
Projects out the variable that is associate with Value.
bool containsVar(Value val) const
Returns true if a variable with the specified Value exists, false otherwise.
void removeVarRange(presburger::VarKind kind, unsigned varStart, unsigned varLimit) override
Removes variables in the column range [varStart, varLimit), and copies any remaining valid data into ...
unsigned insertSymbolVar(unsigned pos, ValueRange vals)
bool findVar(Value val, unsigned *pos, unsigned offset=0) const
Looks up the position of the variable with the specified Value starting with variables at offset offs...
LogicalResult addBound(presburger::BoundType type, unsigned pos, AffineMap boundMap, bool isClosedBound, AddConservativeSemiAffineBounds=AddConservativeSemiAffineBounds::No)
Adds a bound for the variable at the specified position with constraints being drawn from the specifi...
bool areVarsAlignedWithOther(const FlatLinearConstraints &other)
Returns true if this constraint system and other are in the same space, i.e., if they are associated ...
AffineMap computeAlignedMap(AffineMap map, ValueRange operands) const
Align map with this constraint system based on operands.
void getValues(unsigned start, unsigned end, SmallVectorImpl< Value > *values) const
Returns the Values associated with variables in range [start, end).
FlatLinearValueConstraints(unsigned numReservedInequalities, unsigned numReservedEqualities, unsigned numReservedCols, unsigned numDims, unsigned numSymbols, unsigned numLocals, ArrayRef< std::optional< Value >> valArgs)
Constructs a constraint system reserving memory for the specified number of constraints and variables...
void setValue(unsigned pos, Value val)
Sets the Value associated with the pos^th variable.
unsigned insertDimVar(unsigned pos, unsigned num=1)
Insert variables of the specified kind at position pos.
unsigned insertVar(presburger::VarKind kind, unsigned pos, unsigned num=1) override
Insert num variables of the specified kind at position pos.
An integer set representing a conjunction of one or more affine equalities and inequalities.
Definition: IntegerSet.h:44
unsigned getNumDims() const
Definition: IntegerSet.cpp:15
static IntegerSet get(unsigned dimCount, unsigned symbolCount, ArrayRef< AffineExpr > constraints, ArrayRef< bool > eqFlags)
unsigned getNumInputs() const
Definition: IntegerSet.cpp:17
unsigned getNumConstraints() const
Definition: IntegerSet.cpp:21
ArrayRef< AffineExpr > getConstraints() const
Definition: IntegerSet.cpp:41
ArrayRef< bool > getEqFlags() const
Returns the equality bits, which specify whether each of the constraints is an equality or inequality...
Definition: IntegerSet.cpp:51
unsigned getNumSymbols() const
Definition: IntegerSet.cpp:16
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:60
virtual void addLocalFloorDivId(ArrayRef< int64_t > dividend, int64_t divisor, AffineExpr localExpr)
virtual LogicalResult addLocalIdSemiAffine(ArrayRef< int64_t > lhs, ArrayRef< int64_t > rhs, AffineExpr localExpr)
Add a local identifier (needed to flatten a mod, floordiv, ceildiv, mul expr) when the rhs is a symbo...
SimpleAffineExprFlattener(unsigned numDims, unsigned numSymbols)
This class provides an abstraction over the different types of ranges over Values.
Definition: ValueRange.h:381
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:96
Class storing division representation of local variables of a constraint system.
Definition: Utils.h:117
bool hasAllReprs() const
Definition: Utils.h:133
unsigned getNumDivs() const
Definition: Utils.h:125
An Identifier stores a pointer to an object, such as a Value or an Operation.
An IntegerPolyhedron represents the set of points from a PresburgerSpace that satisfy a list of affin...
virtual void swapVar(unsigned posA, unsigned posB)
Swap the posA^th variable with the posB^th variable.
int64_t atEq64(unsigned i, unsigned j) const
The same, but casts to int64_t.
unsigned getVarKindEnd(VarKind kind) const
Return the index at Which the specified kind of vars ends.
std::optional< unsigned > findConstraintWithNonZeroAt(unsigned colIdx, bool isEq) const
Finds a constraint with a non-zero coefficient at colIdx in equality (isEq=true) or inequality (isEq=...
SmallVector< int64_t, 8 > getEquality64(unsigned idx) const
The same, but casts to int64_t.
std::optional< int64_t > getConstantBound64(BoundType type, unsigned pos) const
The same, but casts to int64_t.
unsigned getNumVarKind(VarKind kind) const
Get the number of vars of the specified kind.
void addLocalFloorDiv(ArrayRef< DynamicAPInt > dividend, const DynamicAPInt &divisor)
Adds a new local variable as the floordiv of an affine function of other variables,...
void append(const IntegerRelation &other)
Appends constraints from other into this.
void addEquality(ArrayRef< DynamicAPInt > eq)
Adds an equality from the coefficients specified in eq.
unsigned getNumCols() const
Returns the number of columns in the constraint system.
DivisionRepr getLocalReprs(std::vector< MaybeLocalRepr > *repr=nullptr) const
Returns a DivisonRepr representing the division representation of local variables in the constraint s...
virtual void clearAndCopyFrom(const IntegerRelation &other)
Replaces the contents of this IntegerRelation with other.
void addInequality(ArrayRef< DynamicAPInt > inEq)
Adds an inequality (>= 0) from the coefficients specified in inEq.
unsigned mergeLocalVars(IntegerRelation &other)
Adds additional local vars to the sets such that they both have the union of the local vars in each s...
SmallVector< int64_t, 8 > getInequality64(unsigned idx) const
unsigned getVarKindOffset(VarKind kind) const
Return the index at which the specified kind of vars starts.
virtual void fourierMotzkinEliminate(unsigned pos, bool darkShadow=false, bool *isResultIntegerExact=nullptr)
Eliminates the variable at the specified position using Fourier-Motzkin variable elimination,...
This class represents a multi-affine function with the domain as Z^d, where d is the number of domain...
Definition: PWMAFunction.h:41
Identifier getId(VarKind kind, unsigned pos) const
Get the identifier of pos^th variable of the specified kind.
static PresburgerSpace getSetSpace(unsigned numDims=0, unsigned numSymbols=0, unsigned numLocals=0)
static void printSpace(std::ostream &os, int count)
Definition: RunnerUtils.h:98
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:344
BoundType
The type of bound: equal, lower bound or upper bound.
VarKind
Kind of variable.
MaybeLocalRepr computeSingleVarRepr(const IntegerRelation &cst, ArrayRef< bool > foundRepr, unsigned pos, MutableArrayRef< DynamicAPInt > dividend, DynamicAPInt &divisor)
Returns the MaybeLocalRepr struct which contains the indices of the constraints that can be expressed...
Definition: Utils.cpp:228
Fraction abs(const Fraction &f)
Definition: Fraction.h:107
Include the generated interface declarations.
const FrozenRewritePatternSet GreedyRewriteConfig bool * changed
AffineMap alignAffineMapWithValues(AffineMap map, ValueRange operands, ValueRange dims, ValueRange syms, SmallVector< Value > *newSyms=nullptr)
Re-indexes the dimensions and symbols of an affine map with given operands values to align with dims ...
@ Mod
RHS of mod is always a constant or a symbolic expression with a positive value.
AffineExpr getAffineExprFromFlatForm(ArrayRef< int64_t > flatExprs, unsigned numDims, unsigned numSymbols, ArrayRef< AffineExpr > localExprs, MLIRContext *context)
Constructs an affine expression from a flat ArrayRef.
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
Definition: AffineExpr.cpp:645
LogicalResult getFlattenedAffineExpr(AffineExpr expr, unsigned numDims, unsigned numSymbols, SmallVectorImpl< int64_t > *flattenedExpr, FlatLinearConstraints *cst=nullptr, bool addConservativeSemiAffineBounds=false)
Flattens 'expr' into 'flattenedExpr', which contains the coefficients of the dimensions,...
LogicalResult getFlattenedAffineExprs(AffineMap map, std::vector< SmallVector< int64_t, 8 >> *flattenedExprs, FlatLinearConstraints *cst=nullptr, bool addConservativeSemiAffineBounds=false)
Flattens the result expressions of the map to their corresponding flattened forms and set in 'flatten...
AffineExpr simplifyAffineExpr(AffineExpr expr, unsigned numDims, unsigned numSymbols)
Simplify an affine expression by flattening and some amount of simple analysis.
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
Definition: AffineExpr.cpp:621
LogicalResult getMultiAffineFunctionFromMap(AffineMap map, presburger::MultiAffineFunction &multiAff)
AffineExpr getAffineSymbolExpr(unsigned position, MLIRContext *context)
Definition: AffineExpr.cpp:631
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.