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 /// Compute a representation of `num` identifiers starting at `offset` in `cst`
585 /// as affine expressions involving other known identifiers. Each identifier's
586 /// expression (in terms of known identifiers) is populated into `memo`.
588  MLIRContext *context, unsigned offset,
589  unsigned num,
591  // Initialize dimensional and symbolic variables.
592  for (unsigned i = 0, e = cst.getNumDimVars(); i < e; i++) {
593  if (i < offset)
594  memo[i] = getAffineDimExpr(i, context);
595  else if (i >= offset + num)
596  memo[i] = getAffineDimExpr(i - num, context);
597  }
598  for (unsigned i = cst.getNumDimVars(), e = cst.getNumDimAndSymbolVars();
599  i < e; i++)
600  memo[i] = getAffineSymbolExpr(i - cst.getNumDimVars(), context);
601 
602  bool changed;
603  do {
604  changed = false;
605  // Identify yet unknown variables as constants or mod's / floordiv's of
606  // other variables if possible.
607  for (unsigned pos = 0, f = cst.getNumVars(); pos < f; pos++) {
608  if (memo[pos])
609  continue;
610 
611  auto lbConst = cst.getConstantBound64(BoundType::LB, pos);
612  auto ubConst = cst.getConstantBound64(BoundType::UB, pos);
613  if (lbConst.has_value() && ubConst.has_value()) {
614  // Detect equality to a constant.
615  if (*lbConst == *ubConst) {
616  memo[pos] = getAffineConstantExpr(*lbConst, context);
617  changed = true;
618  continue;
619  }
620 
621  // Detect a variable as modulo of another variable w.r.t a
622  // constant.
623  if (detectAsMod(cst, pos, offset, num, *lbConst, *ubConst, context,
624  memo)) {
625  changed = true;
626  continue;
627  }
628  }
629 
630  // Detect a variable as a floordiv of an affine function of other
631  // variables (divisor is a positive constant).
632  if (detectAsFloorDiv(cst, pos, context, memo)) {
633  changed = true;
634  continue;
635  }
636 
637  // Detect a variable as an expression of other variables.
638  unsigned idx;
639  if (!cst.findConstraintWithNonZeroAt(pos, /*isEq=*/true, &idx)) {
640  continue;
641  }
642 
643  // Build AffineExpr solving for variable 'pos' in terms of all others.
644  auto expr = getAffineConstantExpr(0, context);
645  unsigned j, e;
646  for (j = 0, e = cst.getNumVars(); j < e; ++j) {
647  if (j == pos)
648  continue;
649  int64_t c = cst.atEq64(idx, j);
650  if (c == 0)
651  continue;
652  // If any of the involved IDs hasn't been found yet, we can't proceed.
653  if (!memo[j])
654  break;
655  expr = expr + memo[j] * c;
656  }
657  if (j < e)
658  // Can't construct expression as it depends on a yet uncomputed
659  // variable.
660  continue;
661 
662  // Add constant term to AffineExpr.
663  expr = expr + cst.atEq64(idx, cst.getNumVars());
664  int64_t vPos = cst.atEq64(idx, pos);
665  assert(vPos != 0 && "expected non-zero here");
666  if (vPos > 0)
667  expr = (-expr).floorDiv(vPos);
668  else
669  // vPos < 0.
670  expr = expr.floorDiv(-vPos);
671  // Successfully constructed expression.
672  memo[pos] = expr;
673  changed = true;
674  }
675  // This loop is guaranteed to reach a fixed point - since once an
676  // variable's explicit form is computed (in memo[pos]), it's not updated
677  // again.
678  } while (changed);
679 }
680 
681 /// Computes the lower and upper bounds of the first 'num' dimensional
682 /// variables (starting at 'offset') as affine maps of the remaining
683 /// variables (dimensional and symbolic variables). Local variables are
684 /// themselves explicitly computed as affine functions of other variables in
685 /// this process if needed.
686 void FlatLinearConstraints::getSliceBounds(unsigned offset, unsigned num,
687  MLIRContext *context,
690  bool closedUB) {
691  assert(offset + num <= getNumDimVars() && "invalid range");
692 
693  // Basic simplification.
694  normalizeConstraintsByGCD();
695 
696  LLVM_DEBUG(llvm::dbgs() << "getSliceBounds for variables at positions ["
697  << offset << ", " << offset + num << ")\n");
698  LLVM_DEBUG(dumpPretty());
699 
700  // Record computed/detected variables.
701  SmallVector<AffineExpr, 8> memo(getNumVars());
702  computeUnknownVars(*this, context, offset, num, memo);
703 
704  int64_t ubAdjustment = closedUB ? 0 : 1;
705 
706  // Set the lower and upper bound maps for all the variables that were
707  // computed as affine expressions of the rest as the "detected expr" and
708  // "detected expr + 1" respectively; set the undetected ones to null.
709  std::optional<FlatLinearConstraints> tmpClone;
710  for (unsigned pos = 0; pos < num; pos++) {
711  unsigned numMapDims = getNumDimVars() - num;
712  unsigned numMapSymbols = getNumSymbolVars();
713  AffineExpr expr = memo[pos + offset];
714  if (expr)
715  expr = simplifyAffineExpr(expr, numMapDims, numMapSymbols);
716 
717  AffineMap &lbMap = (*lbMaps)[pos];
718  AffineMap &ubMap = (*ubMaps)[pos];
719 
720  if (expr) {
721  lbMap = AffineMap::get(numMapDims, numMapSymbols, expr);
722  ubMap = AffineMap::get(numMapDims, numMapSymbols, expr + ubAdjustment);
723  } else {
724  // TODO: Whenever there are local variables in the dependence
725  // constraints, we'll conservatively over-approximate, since we don't
726  // always explicitly compute them above (in the while loop).
727  if (getNumLocalVars() == 0) {
728  // Work on a copy so that we don't update this constraint system.
729  if (!tmpClone) {
730  tmpClone.emplace(FlatLinearConstraints(*this));
731  // Removing redundant inequalities is necessary so that we don't get
732  // redundant loop bounds.
733  tmpClone->removeRedundantInequalities();
734  }
735  std::tie(lbMap, ubMap) = tmpClone->getLowerAndUpperBound(
736  pos, offset, num, getNumDimVars(), /*localExprs=*/{}, context,
737  closedUB);
738  }
739 
740  // If the above fails, we'll just use the constant lower bound and the
741  // constant upper bound (if they exist) as the slice bounds.
742  // TODO: being conservative for the moment in cases that
743  // lead to multiple bounds - until getConstDifference in LoopFusion.cpp is
744  // fixed (b/126426796).
745  if (!lbMap || lbMap.getNumResults() != 1) {
746  LLVM_DEBUG(llvm::dbgs()
747  << "WARNING: Potentially over-approximating slice lb\n");
748  auto lbConst = getConstantBound64(BoundType::LB, pos + offset);
749  if (lbConst.has_value()) {
750  lbMap = AffineMap::get(numMapDims, numMapSymbols,
751  getAffineConstantExpr(*lbConst, context));
752  }
753  }
754  if (!ubMap || ubMap.getNumResults() != 1) {
755  LLVM_DEBUG(llvm::dbgs()
756  << "WARNING: Potentially over-approximating slice ub\n");
757  auto ubConst = getConstantBound64(BoundType::UB, pos + offset);
758  if (ubConst.has_value()) {
759  ubMap = AffineMap::get(
760  numMapDims, numMapSymbols,
761  getAffineConstantExpr(*ubConst + ubAdjustment, context));
762  }
763  }
764  }
765 
766  LLVM_DEBUG(llvm::dbgs() << "Slice bounds:\n");
767  LLVM_DEBUG(llvm::dbgs() << "lb map for pos = " << Twine(pos + offset)
768  << ", expr: " << lbMap << '\n');
769  LLVM_DEBUG(llvm::dbgs() << "ub map for pos = " << Twine(pos + offset)
770  << ", expr: " << ubMap << '\n');
771  }
772 }
773 
775  AffineMap map, std::vector<SmallVector<int64_t, 8>> *flattenedExprs,
776  bool addConservativeSemiAffineBounds) {
777  FlatLinearConstraints localCst;
778  if (failed(getFlattenedAffineExprs(map, flattenedExprs, &localCst,
779  addConservativeSemiAffineBounds))) {
780  LLVM_DEBUG(llvm::dbgs()
781  << "composition unimplemented for semi-affine maps\n");
782  return failure();
783  }
784 
785  // Add localCst information.
786  if (localCst.getNumLocalVars() > 0) {
787  unsigned numLocalVars = getNumLocalVars();
788  // Insert local dims of localCst at the beginning.
789  insertLocalVar(/*pos=*/0, /*num=*/localCst.getNumLocalVars());
790  // Insert local dims of `this` at the end of localCst.
791  localCst.appendLocalVar(/*num=*/numLocalVars);
792  // Dimensions of localCst and this constraint set match. Append localCst to
793  // this constraint set.
794  append(localCst);
795  }
796 
797  return success();
798 }
799 
801  BoundType type, unsigned pos, AffineMap boundMap, bool isClosedBound,
802  AddConservativeSemiAffineBounds addSemiAffineBounds) {
803  assert(boundMap.getNumDims() == getNumDimVars() && "dim mismatch");
804  assert(boundMap.getNumSymbols() == getNumSymbolVars() && "symbol mismatch");
805  assert(pos < getNumDimAndSymbolVars() && "invalid position");
806  assert((type != BoundType::EQ || isClosedBound) &&
807  "EQ bound must be closed.");
808 
809  // Equality follows the logic of lower bound except that we add an equality
810  // instead of an inequality.
811  assert((type != BoundType::EQ || boundMap.getNumResults() == 1) &&
812  "single result expected");
813  bool lower = type == BoundType::LB || type == BoundType::EQ;
814 
815  std::vector<SmallVector<int64_t, 8>> flatExprs;
816  if (failed(flattenAlignedMapAndMergeLocals(
817  boundMap, &flatExprs,
818  addSemiAffineBounds == AddConservativeSemiAffineBounds::Yes)))
819  return failure();
820  assert(flatExprs.size() == boundMap.getNumResults());
821 
822  // Add one (in)equality for each result.
823  for (const auto &flatExpr : flatExprs) {
824  SmallVector<int64_t> ineq(getNumCols(), 0);
825  // Dims and symbols.
826  for (unsigned j = 0, e = boundMap.getNumInputs(); j < e; j++) {
827  ineq[j] = lower ? -flatExpr[j] : flatExpr[j];
828  }
829  // Invalid bound: pos appears in `boundMap`.
830  // TODO: This should be an assertion. Fix `addDomainFromSliceMaps` and/or
831  // its callers to prevent invalid bounds from being added.
832  if (ineq[pos] != 0)
833  continue;
834  ineq[pos] = lower ? 1 : -1;
835  // Local columns of `ineq` are at the beginning.
836  unsigned j = getNumDimVars() + getNumSymbolVars();
837  unsigned end = flatExpr.size() - 1;
838  for (unsigned i = boundMap.getNumInputs(); i < end; i++, j++) {
839  ineq[j] = lower ? -flatExpr[i] : flatExpr[i];
840  }
841  // Make the bound closed in if flatExpr is open. The inequality is always
842  // created in the upper bound form, so the adjustment is -1.
843  int64_t boundAdjustment = (isClosedBound || type == BoundType::EQ) ? 0 : -1;
844  // Constant term.
845  ineq[getNumCols() - 1] = (lower ? -flatExpr[flatExpr.size() - 1]
846  : flatExpr[flatExpr.size() - 1]) +
847  boundAdjustment;
848  type == BoundType::EQ ? addEquality(ineq) : addInequality(ineq);
849  }
850 
851  return success();
852 }
853 
855  BoundType type, unsigned pos, AffineMap boundMap,
856  AddConservativeSemiAffineBounds addSemiAffineBounds) {
857  return addBound(type, pos, boundMap,
858  /*isClosedBound=*/type != BoundType::UB, addSemiAffineBounds);
859 }
860 
861 /// Compute an explicit representation for local vars. For all systems coming
862 /// from MLIR integer sets, maps, or expressions where local vars were
863 /// introduced to model floordivs and mods, this always succeeds.
864 LogicalResult
866  MLIRContext *context) const {
867  unsigned numDims = getNumDimVars();
868  unsigned numSyms = getNumSymbolVars();
869 
870  // Initialize dimensional and symbolic variables.
871  for (unsigned i = 0; i < numDims; i++)
872  memo[i] = getAffineDimExpr(i, context);
873  for (unsigned i = numDims, e = numDims + numSyms; i < e; i++)
874  memo[i] = getAffineSymbolExpr(i - numDims, context);
875 
876  bool changed;
877  do {
878  // Each time `changed` is true at the end of this iteration, one or more
879  // local vars would have been detected as floordivs and set in memo; so the
880  // number of null entries in memo[...] strictly reduces; so this converges.
881  changed = false;
882  for (unsigned i = 0, e = getNumLocalVars(); i < e; ++i)
883  if (!memo[numDims + numSyms + i] &&
884  detectAsFloorDiv(*this, /*pos=*/numDims + numSyms + i, context, memo))
885  changed = true;
886  } while (changed);
887 
888  ArrayRef<AffineExpr> localExprs =
889  ArrayRef<AffineExpr>(memo).take_back(getNumLocalVars());
890  return success(
891  llvm::all_of(localExprs, [](AffineExpr expr) { return expr; }));
892 }
893 
895  if (getNumConstraints() == 0)
896  // Return universal set (always true): 0 == 0.
897  return IntegerSet::get(getNumDimVars(), getNumSymbolVars(),
898  getAffineConstantExpr(/*constant=*/0, context),
899  /*eqFlags=*/true);
900 
901  // Construct local references.
902  SmallVector<AffineExpr, 8> memo(getNumVars(), AffineExpr());
903 
904  if (failed(computeLocalVars(memo, context))) {
905  // Check if the local variables without an explicit representation have
906  // zero coefficients everywhere.
907  SmallVector<unsigned> noLocalRepVars;
908  unsigned numDimsSymbols = getNumDimAndSymbolVars();
909  for (unsigned i = numDimsSymbols, e = getNumVars(); i < e; ++i) {
910  if (!memo[i] && !isColZero(/*pos=*/i))
911  noLocalRepVars.push_back(i - numDimsSymbols);
912  }
913  if (!noLocalRepVars.empty()) {
914  LLVM_DEBUG({
915  llvm::dbgs() << "local variables at position(s) ";
916  llvm::interleaveComma(noLocalRepVars, llvm::dbgs());
917  llvm::dbgs() << " do not have an explicit representation in:\n";
918  this->dump();
919  });
920  return IntegerSet();
921  }
922  }
923 
924  ArrayRef<AffineExpr> localExprs =
925  ArrayRef<AffineExpr>(memo).take_back(getNumLocalVars());
926 
927  // Construct the IntegerSet from the equalities/inequalities.
928  unsigned numDims = getNumDimVars();
929  unsigned numSyms = getNumSymbolVars();
930 
931  SmallVector<bool, 16> eqFlags(getNumConstraints());
932  std::fill(eqFlags.begin(), eqFlags.begin() + getNumEqualities(), true);
933  std::fill(eqFlags.begin() + getNumEqualities(), eqFlags.end(), false);
934 
936  exprs.reserve(getNumConstraints());
937 
938  for (unsigned i = 0, e = getNumEqualities(); i < e; ++i)
939  exprs.push_back(getAffineExprFromFlatForm(getEquality64(i), numDims,
940  numSyms, localExprs, context));
941  for (unsigned i = 0, e = getNumInequalities(); i < e; ++i)
942  exprs.push_back(getAffineExprFromFlatForm(getInequality64(i), numDims,
943  numSyms, localExprs, context));
944  return IntegerSet::get(numDims, numSyms, exprs, eqFlags);
945 }
946 
947 //===----------------------------------------------------------------------===//
948 // FlatLinearValueConstraints
949 //===----------------------------------------------------------------------===//
950 
951 // Construct from an IntegerSet.
953  ValueRange operands)
954  : FlatLinearConstraints(set.getNumInequalities(), set.getNumEqualities(),
955  set.getNumDims() + set.getNumSymbols() + 1,
956  set.getNumDims(), set.getNumSymbols(),
957  /*numLocals=*/0) {
958  assert((operands.empty() || set.getNumInputs() == operands.size()) &&
959  "operand count mismatch");
960  // Set the values for the non-local variables.
961  for (unsigned i = 0, e = operands.size(); i < e; ++i)
962  setValue(i, operands[i]);
963 
964  // Flatten expressions and add them to the constraint system.
965  std::vector<SmallVector<int64_t, 8>> flatExprs;
966  FlatLinearConstraints localVarCst;
967  if (failed(getFlattenedAffineExprs(set, &flatExprs, &localVarCst))) {
968  assert(false && "flattening unimplemented for semi-affine integer sets");
969  return;
970  }
971  assert(flatExprs.size() == set.getNumConstraints());
972  insertVar(VarKind::Local, getNumVarKind(VarKind::Local),
973  /*num=*/localVarCst.getNumLocalVars());
974 
975  for (unsigned i = 0, e = flatExprs.size(); i < e; ++i) {
976  const auto &flatExpr = flatExprs[i];
977  assert(flatExpr.size() == getNumCols());
978  if (set.getEqFlags()[i]) {
979  addEquality(flatExpr);
980  } else {
981  addInequality(flatExpr);
982  }
983  }
984  // Add the other constraints involving local vars from flattening.
985  append(localVarCst);
986 }
987 
989  unsigned pos = getNumDimVars();
990  return insertVar(VarKind::SetDim, pos, vals);
991 }
992 
994  unsigned pos = getNumSymbolVars();
995  return insertVar(VarKind::Symbol, pos, vals);
996 }
997 
999  ValueRange vals) {
1000  return insertVar(VarKind::SetDim, pos, vals);
1001 }
1002 
1004  ValueRange vals) {
1005  return insertVar(VarKind::Symbol, pos, vals);
1006 }
1007 
1009  unsigned num) {
1010  unsigned absolutePos = IntegerPolyhedron::insertVar(kind, pos, num);
1011 
1012  return absolutePos;
1013 }
1014 
1016  ValueRange vals) {
1017  assert(!vals.empty() && "expected ValueRange with Values.");
1018  assert(kind != VarKind::Local &&
1019  "values cannot be attached to local variables.");
1020  unsigned num = vals.size();
1021  unsigned absolutePos = IntegerPolyhedron::insertVar(kind, pos, num);
1022 
1023  // If a Value is provided, insert it; otherwise use std::nullopt.
1024  for (unsigned i = 0, e = vals.size(); i < e; ++i)
1025  if (vals[i])
1026  setValue(absolutePos + i, vals[i]);
1027 
1028  return absolutePos;
1029 }
1030 
1031 /// Checks if two constraint systems are in the same space, i.e., if they are
1032 /// associated with the same set of variables, appearing in the same order.
1034  const FlatLinearValueConstraints &b) {
1035  if (a.getNumDomainVars() != b.getNumDomainVars() ||
1036  a.getNumRangeVars() != b.getNumRangeVars() ||
1038  return false;
1040  bMaybeValues = b.getMaybeValues();
1041  return std::equal(aMaybeValues.begin(), aMaybeValues.end(),
1042  bMaybeValues.begin(), bMaybeValues.end());
1043 }
1044 
1045 /// Calls areVarsAligned to check if two constraint systems have the same set
1046 /// of variables in the same order.
1048  const FlatLinearConstraints &other) {
1049  return areVarsAligned(*this, other);
1050 }
1051 
1052 /// Checks if the SSA values associated with `cst`'s variables in range
1053 /// [start, end) are unique.
1054 static bool LLVM_ATTRIBUTE_UNUSED areVarsUnique(
1055  const FlatLinearValueConstraints &cst, unsigned start, unsigned end) {
1056 
1057  assert(start <= cst.getNumDimAndSymbolVars() &&
1058  "Start position out of bounds");
1059  assert(end <= cst.getNumDimAndSymbolVars() && "End position out of bounds");
1060 
1061  if (start >= end)
1062  return true;
1063 
1064  SmallPtrSet<Value, 8> uniqueVars;
1065  SmallVector<std::optional<Value>, 8> maybeValuesAll = cst.getMaybeValues();
1066  ArrayRef<std::optional<Value>> maybeValues = {maybeValuesAll.data() + start,
1067  maybeValuesAll.data() + end};
1068 
1069  for (std::optional<Value> val : maybeValues)
1070  if (val && !uniqueVars.insert(*val).second)
1071  return false;
1072 
1073  return true;
1074 }
1075 
1076 /// Checks if the SSA values associated with `cst`'s variables are unique.
1077 static bool LLVM_ATTRIBUTE_UNUSED
1079  return areVarsUnique(cst, 0, cst.getNumDimAndSymbolVars());
1080 }
1081 
1082 /// Checks if the SSA values associated with `cst`'s variables of kind `kind`
1083 /// are unique.
1084 static bool LLVM_ATTRIBUTE_UNUSED
1086 
1087  if (kind == VarKind::SetDim)
1088  return areVarsUnique(cst, 0, cst.getNumDimVars());
1089  if (kind == VarKind::Symbol)
1090  return areVarsUnique(cst, cst.getNumDimVars(),
1091  cst.getNumDimAndSymbolVars());
1092  llvm_unreachable("Unexpected VarKind");
1093 }
1094 
1095 /// Merge and align the variables of A and B starting at 'offset', so that
1096 /// both constraint systems get the union of the contained variables that is
1097 /// dimension-wise and symbol-wise unique; both constraint systems are updated
1098 /// so that they have the union of all variables, with A's original
1099 /// variables appearing first followed by any of B's variables that didn't
1100 /// appear in A. Local variables in B that have the same division
1101 /// representation as local variables in A are merged into one. We allow A
1102 /// and B to have non-unique values for their variables; in such cases, they are
1103 /// still aligned with the variables appearing first aligned with those
1104 /// appearing first in the other system from left to right.
1105 // E.g.: Input: A has ((%i, %j) [%M, %N]) and B has (%k, %j) [%P, %N, %M])
1106 // Output: both A, B have (%i, %j, %k) [%M, %N, %P]
1107 static void mergeAndAlignVars(unsigned offset, FlatLinearValueConstraints *a,
1109  assert(offset <= a->getNumDimVars() && offset <= b->getNumDimVars());
1110 
1111  assert(llvm::all_of(
1112  llvm::drop_begin(a->getMaybeValues(), offset),
1113  [](const std::optional<Value> &var) { return var.has_value(); }));
1114 
1115  assert(llvm::all_of(
1116  llvm::drop_begin(b->getMaybeValues(), offset),
1117  [](const std::optional<Value> &var) { return var.has_value(); }));
1118 
1119  SmallVector<Value, 4> aDimValues;
1120  a->getValues(offset, a->getNumDimVars(), &aDimValues);
1121 
1122  {
1123  // Merge dims from A into B.
1124  unsigned d = offset;
1125  for (Value aDimValue : aDimValues) {
1126  unsigned loc;
1127  // Find from the position `d` since we'd like to also consider the
1128  // possibility of multiple variables with the same `Value`. We align with
1129  // the next appearing one.
1130  if (b->findVar(aDimValue, &loc, d)) {
1131  assert(loc >= offset && "A's dim appears in B's aligned range");
1132  assert(loc < b->getNumDimVars() &&
1133  "A's dim appears in B's non-dim position");
1134  b->swapVar(d, loc);
1135  } else {
1136  b->insertDimVar(d, aDimValue);
1137  }
1138  d++;
1139  }
1140  // Dimensions that are in B, but not in A, are added at the end.
1141  for (unsigned t = a->getNumDimVars(), e = b->getNumDimVars(); t < e; t++) {
1142  a->appendDimVar(b->getValue(t));
1143  }
1144  assert(a->getNumDimVars() == b->getNumDimVars() &&
1145  "expected same number of dims");
1146  }
1147 
1148  // Merge and align symbols of A and B
1149  a->mergeSymbolVars(*b);
1150  // Merge and align locals of A and B
1151  a->mergeLocalVars(*b);
1152 
1153  assert(areVarsAligned(*a, *b) && "IDs expected to be aligned");
1154 }
1155 
1156 // Call 'mergeAndAlignVars' to align constraint systems of 'this' and 'other'.
1158  unsigned offset, FlatLinearValueConstraints *other) {
1159  mergeAndAlignVars(offset, this, other);
1160 }
1161 
1162 /// Merge and align symbols of `this` and `other` such that both get union of
1163 /// of symbols. Existing symbols need not be unique; they will be aligned from
1164 /// left to right with duplicates aligned in the same order. Symbols with Value
1165 /// as `None` are considered to be inequal to all other symbols.
1167  FlatLinearValueConstraints &other) {
1168 
1169  SmallVector<Value, 4> aSymValues;
1170  getValues(getNumDimVars(), getNumDimAndSymbolVars(), &aSymValues);
1171 
1172  // Merge symbols: merge symbols into `other` first from `this`.
1173  unsigned s = other.getNumDimVars();
1174  for (Value aSymValue : aSymValues) {
1175  unsigned loc;
1176  // If the var is a symbol in `other`, then align it, otherwise assume that
1177  // it is a new symbol. Search in `other` starting at position `s` since the
1178  // left of it is aligned.
1179  if (other.findVar(aSymValue, &loc, s) && loc >= other.getNumDimVars() &&
1180  loc < other.getNumDimAndSymbolVars())
1181  other.swapVar(s, loc);
1182  else
1183  other.insertSymbolVar(s - other.getNumDimVars(), aSymValue);
1184  s++;
1185  }
1186 
1187  // Symbols that are in other, but not in this, are added at the end.
1188  for (unsigned t = other.getNumDimVars() + getNumSymbolVars(),
1189  e = other.getNumDimAndSymbolVars();
1190  t < e; t++)
1192 
1193  assert(getNumSymbolVars() == other.getNumSymbolVars() &&
1194  "expected same number of symbols");
1195 }
1196 
1198  unsigned varLimit) {
1199  IntegerPolyhedron::removeVarRange(kind, varStart, varLimit);
1200 }
1201 
1202 AffineMap
1204  ValueRange operands) const {
1205  assert(map.getNumInputs() == operands.size() && "number of inputs mismatch");
1206 
1207  SmallVector<Value> dims, syms;
1208 #ifndef NDEBUG
1209  SmallVector<Value> newSyms;
1210  SmallVector<Value> *newSymsPtr = &newSyms;
1211 #else
1212  SmallVector<Value> *newSymsPtr = nullptr;
1213 #endif // NDEBUG
1214 
1215  dims.reserve(getNumDimVars());
1216  syms.reserve(getNumSymbolVars());
1217  for (unsigned i = 0, e = getNumVarKind(VarKind::SetDim); i < e; ++i) {
1218  Identifier id = space.getId(VarKind::SetDim, i);
1219  dims.push_back(id.hasValue() ? Value(id.getValue<Value>()) : Value());
1220  }
1221  for (unsigned i = 0, e = getNumVarKind(VarKind::Symbol); i < e; ++i) {
1222  Identifier id = space.getId(VarKind::Symbol, i);
1223  syms.push_back(id.hasValue() ? Value(id.getValue<Value>()) : Value());
1224  }
1225 
1226  AffineMap alignedMap =
1227  alignAffineMapWithValues(map, operands, dims, syms, newSymsPtr);
1228  // All symbols are already part of this FlatAffineValueConstraints.
1229  assert(syms.size() == newSymsPtr->size() && "unexpected new/missing symbols");
1230  assert(std::equal(syms.begin(), syms.end(), newSymsPtr->begin()) &&
1231  "unexpected new/missing symbols");
1232  return alignedMap;
1233 }
1234 
1236  unsigned offset) const {
1238  for (unsigned i = offset, e = maybeValues.size(); i < e; ++i)
1239  if (maybeValues[i] && maybeValues[i].value() == val) {
1240  *pos = i;
1241  return true;
1242  }
1243  return false;
1244 }
1245 
1247  unsigned pos;
1248  return findVar(val, &pos, 0);
1249 }
1250 
1252  int64_t value) {
1253  unsigned pos;
1254  if (!findVar(val, &pos))
1255  // This is a pre-condition for this method.
1256  assert(0 && "var not found");
1257  addBound(type, pos, value);
1258 }
1259 
1260 void FlatLinearConstraints::printSpace(raw_ostream &os) const {
1262  os << "(";
1263  for (unsigned i = 0, e = getNumDimAndSymbolVars(); i < e; i++)
1264  os << "None\t";
1265  for (unsigned i = getVarKindOffset(VarKind::Local),
1266  e = getVarKindEnd(VarKind::Local);
1267  i < e; ++i)
1268  os << "Local\t";
1269  os << "const)\n";
1270 }
1271 
1272 void FlatLinearValueConstraints::printSpace(raw_ostream &os) const {
1274  os << "(";
1275  for (unsigned i = 0, e = getNumDimAndSymbolVars(); i < e; i++) {
1276  if (hasValue(i))
1277  os << "Value\t";
1278  else
1279  os << "None\t";
1280  }
1281  for (unsigned i = getVarKindOffset(VarKind::Local),
1282  e = getVarKindEnd(VarKind::Local);
1283  i < e; ++i)
1284  os << "Local\t";
1285  os << "const)\n";
1286 }
1287 
1289  unsigned pos;
1290  bool ret = findVar(val, &pos);
1291  assert(ret);
1292  (void)ret;
1294 }
1295 
1297  const FlatLinearValueConstraints &otherCst) {
1298  assert(otherCst.getNumDimVars() == getNumDimVars() && "dims mismatch");
1300  otherMaybeValues =
1301  otherCst.getMaybeValues();
1302  assert(std::equal(maybeValues.begin(), maybeValues.begin() + getNumDimVars(),
1303  otherMaybeValues.begin(),
1304  otherMaybeValues.begin() + getNumDimVars()) &&
1305  "dim values mismatch");
1306  assert(otherCst.getNumLocalVars() == 0 && "local vars not supported here");
1307  assert(getNumLocalVars() == 0 && "local vars not supported yet here");
1308 
1309  // Align `other` to this.
1310  if (!areVarsAligned(*this, otherCst)) {
1311  FlatLinearValueConstraints otherCopy(otherCst);
1312  mergeAndAlignVars(/*offset=*/getNumDimVars(), this, &otherCopy);
1313  return IntegerPolyhedron::unionBoundingBox(otherCopy);
1314  }
1315 
1316  return IntegerPolyhedron::unionBoundingBox(otherCst);
1317 }
1318 
1319 //===----------------------------------------------------------------------===//
1320 // Helper functions
1321 //===----------------------------------------------------------------------===//
1322 
1324  ValueRange dims, ValueRange syms,
1325  SmallVector<Value> *newSyms) {
1326  assert(operands.size() == map.getNumInputs() &&
1327  "expected same number of operands and map inputs");
1328  MLIRContext *ctx = map.getContext();
1329  Builder builder(ctx);
1330  SmallVector<AffineExpr> dimReplacements(map.getNumDims(), {});
1331  unsigned numSymbols = syms.size();
1332  SmallVector<AffineExpr> symReplacements(map.getNumSymbols(), {});
1333  if (newSyms) {
1334  newSyms->clear();
1335  newSyms->append(syms.begin(), syms.end());
1336  }
1337 
1338  for (const auto &operand : llvm::enumerate(operands)) {
1339  // Compute replacement dim/sym of operand.
1340  AffineExpr replacement;
1341  auto dimIt = llvm::find(dims, operand.value());
1342  auto symIt = llvm::find(syms, operand.value());
1343  if (dimIt != dims.end()) {
1344  replacement =
1345  builder.getAffineDimExpr(std::distance(dims.begin(), dimIt));
1346  } else if (symIt != syms.end()) {
1347  replacement =
1348  builder.getAffineSymbolExpr(std::distance(syms.begin(), symIt));
1349  } else {
1350  // This operand is neither a dimension nor a symbol. Add it as a new
1351  // symbol.
1352  replacement = builder.getAffineSymbolExpr(numSymbols++);
1353  if (newSyms)
1354  newSyms->push_back(operand.value());
1355  }
1356  // Add to corresponding replacements vector.
1357  if (operand.index() < map.getNumDims()) {
1358  dimReplacements[operand.index()] = replacement;
1359  } else {
1360  symReplacements[operand.index() - map.getNumDims()] = replacement;
1361  }
1362  }
1363 
1364  return map.replaceDimsAndSymbols(dimReplacements, symReplacements,
1365  dims.size(), numSymbols);
1366 }
1367 
1368 LogicalResult
1370  MultiAffineFunction &multiAff) {
1372  std::vector<SmallVector<int64_t, 8>> flattenedExprs;
1373  LogicalResult result = getFlattenedAffineExprs(map, &flattenedExprs, &cst);
1374 
1375  if (result.failed())
1376  return failure();
1377 
1378  DivisionRepr divs = cst.getLocalReprs();
1379  assert(divs.hasAllReprs() &&
1380  "AffineMap cannot produce divs without local representation");
1381 
1382  // TODO: We shouldn't have to do this conversion.
1384  map.getNumInputs() + divs.getNumDivs() + 1);
1385  for (unsigned i = 0, e = flattenedExprs.size(); i < e; ++i)
1386  for (unsigned j = 0, f = flattenedExprs[i].size(); j < f; ++j)
1387  mat(i, j) = flattenedExprs[i][j];
1388 
1389  multiAff = MultiAffineFunction(
1390  PresburgerSpace::getRelationSpace(map.getNumDims(), map.getNumResults(),
1391  map.getNumSymbols(), divs.getNumDivs()),
1392  mat, divs);
1393 
1394  return success();
1395 }
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 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...
Base type for affine expression.
Definition: AffineExpr.h:68
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.
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< 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...
bool findConstraintWithNonZeroAt(unsigned colIdx, bool isEq, unsigned *rowIdx) const
Searches for a constraint with a non-zero coefficient at colIdx in equality (isEq=true) or inequality...
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:641
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:617
LogicalResult getMultiAffineFunctionFromMap(AffineMap map, presburger::MultiAffineFunction &multiAff)
AffineExpr getAffineSymbolExpr(unsigned position, MLIRContext *context)
Definition: AffineExpr.cpp:627
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.