xref: /llvm-project/mlir/lib/Dialect/Affine/Analysis/AffineStructures.cpp (revision 0fb216fb2fbb49c1fe90c1c3267873a100b1c356)
1 //===- AffineStructures.cpp - MLIR Affine Structures Class-----------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Structures for affine/polyhedral analysis of affine dialect ops.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "mlir/Dialect/Affine/Analysis/AffineStructures.h"
14 #include "mlir/Analysis/Presburger/IntegerRelation.h"
15 #include "mlir/Analysis/Presburger/LinearTransform.h"
16 #include "mlir/Analysis/Presburger/Simplex.h"
17 #include "mlir/Analysis/Presburger/Utils.h"
18 #include "mlir/Dialect/Affine/IR/AffineOps.h"
19 #include "mlir/Dialect/Affine/IR/AffineValueMap.h"
20 #include "mlir/Dialect/Utils/StaticValueUtils.h"
21 #include "mlir/IR/AffineExprVisitor.h"
22 #include "mlir/IR/IntegerSet.h"
23 #include "mlir/Support/LLVM.h"
24 #include "llvm/ADT/STLExtras.h"
25 #include "llvm/ADT/SmallPtrSet.h"
26 #include "llvm/ADT/SmallVector.h"
27 #include "llvm/Support/Debug.h"
28 #include "llvm/Support/raw_ostream.h"
29 #include <optional>
30 
31 #define DEBUG_TYPE "affine-structures"
32 
33 using namespace mlir;
34 using namespace affine;
35 using namespace presburger;
36 
37 
addInductionVarOrTerminalSymbol(Value val)38 void FlatAffineValueConstraints::addInductionVarOrTerminalSymbol(Value val) {
39   if (containsVar(val))
40     return;
41 
42   // Caller is expected to fully compose map/operands if necessary.
43   assert((isTopLevelValue(val) || isAffineInductionVar(val)) &&
44          "non-terminal symbol / loop IV expected");
45   // Outer loop IVs could be used in forOp's bounds.
46   if (auto loop = getForInductionVarOwner(val)) {
47     appendDimVar(val);
48     if (failed(this->addAffineForOpDomain(loop)))
49       LLVM_DEBUG(
50           loop.emitWarning("failed to add domain info to constraint system"));
51     return;
52   }
53   if (auto parallel = getAffineParallelInductionVarOwner(val)) {
54     appendDimVar(parallel.getIVs());
55     if (failed(this->addAffineParallelOpDomain(parallel)))
56       LLVM_DEBUG(parallel.emitWarning(
57           "failed to add domain info to constraint system"));
58     return;
59   }
60 
61   // Add top level symbol.
62   appendSymbolVar(val);
63   // Check if the symbol is a constant.
64   if (std::optional<int64_t> constOp = getConstantIntValue(val))
65     addBound(BoundType::EQ, val, constOp.value());
66 }
67 
68 LogicalResult
addAffineForOpDomain(AffineForOp forOp)69 FlatAffineValueConstraints::addAffineForOpDomain(AffineForOp forOp) {
70   unsigned pos;
71   // Pre-condition for this method.
72   if (!findVar(forOp.getInductionVar(), &pos)) {
73     assert(false && "Value not found");
74     return failure();
75   }
76 
77   int64_t step = forOp.getStepAsInt();
78   if (step != 1) {
79     if (!forOp.hasConstantLowerBound())
80       LLVM_DEBUG(forOp.emitWarning("domain conservatively approximated"));
81     else {
82       // Add constraints for the stride.
83       // (iv - lb) % step = 0 can be written as:
84       // (iv - lb) - step * q = 0 where q = (iv - lb) / step.
85       // Add local variable 'q' and add the above equality.
86       // The first constraint is q = (iv - lb) floordiv step
87       SmallVector<int64_t, 8> dividend(getNumCols(), 0);
88       int64_t lb = forOp.getConstantLowerBound();
89       dividend[pos] = 1;
90       dividend.back() -= lb;
91       addLocalFloorDiv(dividend, step);
92       // Second constraint: (iv - lb) - step * q = 0.
93       SmallVector<int64_t, 8> eq(getNumCols(), 0);
94       eq[pos] = 1;
95       eq.back() -= lb;
96       // For the local var just added above.
97       eq[getNumCols() - 2] = -step;
98       addEquality(eq);
99     }
100   }
101 
102   if (forOp.hasConstantLowerBound()) {
103     addBound(BoundType::LB, pos, forOp.getConstantLowerBound());
104   } else {
105     // Non-constant lower bound case.
106     if (failed(addBound(BoundType::LB, pos, forOp.getLowerBoundMap(),
107                         forOp.getLowerBoundOperands())))
108       return failure();
109   }
110 
111   if (forOp.hasConstantUpperBound()) {
112     addBound(BoundType::UB, pos, forOp.getConstantUpperBound() - 1);
113     return success();
114   }
115   // Non-constant upper bound case.
116   return addBound(BoundType::UB, pos, forOp.getUpperBoundMap(),
117                   forOp.getUpperBoundOperands());
118 }
119 
addAffineParallelOpDomain(AffineParallelOp parallelOp)120 LogicalResult FlatAffineValueConstraints::addAffineParallelOpDomain(
121     AffineParallelOp parallelOp) {
122   size_t ivPos = 0;
123   for (Value iv : parallelOp.getIVs()) {
124     unsigned pos;
125     if (!findVar(iv, &pos)) {
126       assert(false && "variable expected for the IV value");
127       return failure();
128     }
129 
130     AffineMap lowerBound = parallelOp.getLowerBoundMap(ivPos);
131     if (lowerBound.isConstant())
132       addBound(BoundType::LB, pos, lowerBound.getSingleConstantResult());
133     else if (failed(addBound(BoundType::LB, pos, lowerBound,
134                              parallelOp.getLowerBoundsOperands())))
135       return failure();
136 
137     auto upperBound = parallelOp.getUpperBoundMap(ivPos);
138     if (upperBound.isConstant())
139       addBound(BoundType::UB, pos, upperBound.getSingleConstantResult() - 1);
140     else if (failed(addBound(BoundType::UB, pos, upperBound,
141                              parallelOp.getUpperBoundsOperands())))
142       return failure();
143     ++ivPos;
144   }
145   return success();
146 }
147 
148 LogicalResult
addDomainFromSliceMaps(ArrayRef<AffineMap> lbMaps,ArrayRef<AffineMap> ubMaps,ArrayRef<Value> operands)149 FlatAffineValueConstraints::addDomainFromSliceMaps(ArrayRef<AffineMap> lbMaps,
150                                                    ArrayRef<AffineMap> ubMaps,
151                                                    ArrayRef<Value> operands) {
152   assert(lbMaps.size() == ubMaps.size());
153   assert(lbMaps.size() <= getNumDimVars());
154 
155   for (unsigned i = 0, e = lbMaps.size(); i < e; ++i) {
156     AffineMap lbMap = lbMaps[i];
157     AffineMap ubMap = ubMaps[i];
158     assert(!lbMap || lbMap.getNumInputs() == operands.size());
159     assert(!ubMap || ubMap.getNumInputs() == operands.size());
160 
161     // Check if this slice is just an equality along this dimension. If so,
162     // retrieve the existing loop it equates to and add it to the system.
163     if (lbMap && ubMap && lbMap.getNumResults() == 1 &&
164         ubMap.getNumResults() == 1 &&
165         lbMap.getResult(0) + 1 == ubMap.getResult(0) &&
166         // The condition above will be true for maps describing a single
167         // iteration (e.g., lbMap.getResult(0) = 0, ubMap.getResult(0) = 1).
168         // Make sure we skip those cases by checking that the lb result is not
169         // just a constant.
170         !isa<AffineConstantExpr>(lbMap.getResult(0))) {
171       // Limited support: we expect the lb result to be just a loop dimension.
172       // Not supported otherwise for now.
173       AffineDimExpr result = dyn_cast<AffineDimExpr>(lbMap.getResult(0));
174       if (!result)
175         return failure();
176 
177       AffineForOp loop =
178           getForInductionVarOwner(operands[result.getPosition()]);
179       if (!loop)
180         return failure();
181 
182       if (failed(addAffineForOpDomain(loop)))
183         return failure();
184       continue;
185     }
186 
187     // This slice refers to a loop that doesn't exist in the IR yet. Add its
188     // bounds to the system assuming its dimension variable position is the
189     // same as the position of the loop in the loop nest.
190     if (lbMap && failed(addBound(BoundType::LB, i, lbMap, operands)))
191       return failure();
192     if (ubMap && failed(addBound(BoundType::UB, i, ubMap, operands)))
193       return failure();
194   }
195   return success();
196 }
197 
addAffineIfOpDomain(AffineIfOp ifOp)198 void FlatAffineValueConstraints::addAffineIfOpDomain(AffineIfOp ifOp) {
199   IntegerSet set = ifOp.getIntegerSet();
200   // Canonicalize set and operands to ensure unique values for
201   // FlatAffineValueConstraints below and for early simplification.
202   SmallVector<Value> operands(ifOp.getOperands());
203   canonicalizeSetAndOperands(&set, &operands);
204 
205   // Create the base constraints from the integer set attached to ifOp.
206   FlatAffineValueConstraints cst(set, operands);
207 
208   // Merge the constraints from ifOp to the current domain. We need first merge
209   // and align the IDs from both constraints, and then append the constraints
210   // from the ifOp into the current one.
211   mergeAndAlignVarsWithOther(0, &cst);
212   append(cst);
213 }
214 
addBound(BoundType type,unsigned pos,AffineMap boundMap,ValueRange boundOperands)215 LogicalResult FlatAffineValueConstraints::addBound(BoundType type, unsigned pos,
216                                                    AffineMap boundMap,
217                                                    ValueRange boundOperands) {
218   // Fully compose map and operands; canonicalize and simplify so that we
219   // transitively get to terminal symbols or loop IVs.
220   auto map = boundMap;
221   SmallVector<Value, 4> operands(boundOperands.begin(), boundOperands.end());
222   fullyComposeAffineMapAndOperands(&map, &operands);
223   map = simplifyAffineMap(map);
224   canonicalizeMapAndOperands(&map, &operands);
225   for (auto operand : operands)
226     addInductionVarOrTerminalSymbol(operand);
227   return addBound(type, pos, computeAlignedMap(map, operands));
228 }
229 
230 // Adds slice lower bounds represented by lower bounds in 'lbMaps' and upper
231 // bounds in 'ubMaps' to each value in `values' that appears in the constraint
232 // system. Note that both lower/upper bounds share the same operand list
233 // 'operands'.
234 // This function assumes 'values.size' == 'lbMaps.size' == 'ubMaps.size', and
235 // skips any null AffineMaps in 'lbMaps' or 'ubMaps'.
236 // Note that both lower/upper bounds use operands from 'operands'.
237 // Returns failure for unimplemented cases such as semi-affine expressions or
238 // expressions with mod/floordiv.
addSliceBounds(ArrayRef<Value> values,ArrayRef<AffineMap> lbMaps,ArrayRef<AffineMap> ubMaps,ArrayRef<Value> operands)239 LogicalResult FlatAffineValueConstraints::addSliceBounds(
240     ArrayRef<Value> values, ArrayRef<AffineMap> lbMaps,
241     ArrayRef<AffineMap> ubMaps, ArrayRef<Value> operands) {
242   assert(values.size() == lbMaps.size());
243   assert(lbMaps.size() == ubMaps.size());
244 
245   for (unsigned i = 0, e = lbMaps.size(); i < e; ++i) {
246     unsigned pos;
247     if (!findVar(values[i], &pos))
248       continue;
249 
250     AffineMap lbMap = lbMaps[i];
251     AffineMap ubMap = ubMaps[i];
252     assert(!lbMap || lbMap.getNumInputs() == operands.size());
253     assert(!ubMap || ubMap.getNumInputs() == operands.size());
254 
255     // Check if this slice is just an equality along this dimension.
256     if (lbMap && ubMap && lbMap.getNumResults() == 1 &&
257         ubMap.getNumResults() == 1 &&
258         lbMap.getResult(0) + 1 == ubMap.getResult(0)) {
259       if (failed(addBound(BoundType::EQ, pos, lbMap, operands)))
260         return failure();
261       continue;
262     }
263 
264     // If lower or upper bound maps are null or provide no results, it implies
265     // that the source loop was not at all sliced, and the entire loop will be a
266     // part of the slice.
267     if (lbMap && lbMap.getNumResults() != 0 && ubMap &&
268         ubMap.getNumResults() != 0) {
269       if (failed(addBound(BoundType::LB, pos, lbMap, operands)))
270         return failure();
271       if (failed(addBound(BoundType::UB, pos, ubMap, operands)))
272         return failure();
273     } else {
274       auto loop = getForInductionVarOwner(values[i]);
275       if (failed(this->addAffineForOpDomain(loop)))
276         return failure();
277     }
278   }
279   return success();
280 }
281 
282 LogicalResult
composeMap(const AffineValueMap * vMap)283 FlatAffineValueConstraints::composeMap(const AffineValueMap *vMap) {
284   return composeMatchingMap(
285       computeAlignedMap(vMap->getAffineMap(), vMap->getOperands()));
286 }
287 
288 // Turn a symbol into a dimension.
turnSymbolIntoDim(FlatAffineValueConstraints * cst,Value value)289 static void turnSymbolIntoDim(FlatAffineValueConstraints *cst, Value value) {
290   unsigned pos;
291   if (cst->findVar(value, &pos) && pos >= cst->getNumDimVars() &&
292       pos < cst->getNumDimAndSymbolVars()) {
293     cst->swapVar(pos, cst->getNumDimVars());
294     cst->setDimSymbolSeparation(cst->getNumSymbolVars() - 1);
295   }
296 }
297 
298 // Changes all symbol variables which are loop IVs to dim variables.
convertLoopIVSymbolsToDims()299 void FlatAffineValueConstraints::convertLoopIVSymbolsToDims() {
300   // Gather all symbols which are loop IVs.
301   SmallVector<Value, 4> loopIVs;
302   for (unsigned i = getNumDimVars(), e = getNumDimAndSymbolVars(); i < e; i++) {
303     if (hasValue(i) && getForInductionVarOwner(getValue(i)))
304       loopIVs.push_back(getValue(i));
305   }
306   // Turn each symbol in 'loopIVs' into a dim variable.
307   for (auto iv : loopIVs) {
308     turnSymbolIntoDim(this, iv);
309   }
310 }
311 
getIneqAsAffineValueMap(unsigned pos,unsigned ineqPos,AffineValueMap & vmap,MLIRContext * context) const312 void FlatAffineValueConstraints::getIneqAsAffineValueMap(
313     unsigned pos, unsigned ineqPos, AffineValueMap &vmap,
314     MLIRContext *context) const {
315   unsigned numDims = getNumDimVars();
316   unsigned numSyms = getNumSymbolVars();
317 
318   assert(pos < numDims && "invalid position");
319   assert(ineqPos < getNumInequalities() && "invalid inequality position");
320 
321   // Get expressions for local vars.
322   SmallVector<AffineExpr, 8> memo(getNumVars(), AffineExpr());
323   if (failed(computeLocalVars(memo, context)))
324     assert(false &&
325            "one or more local exprs do not have an explicit representation");
326   auto localExprs = ArrayRef<AffineExpr>(memo).take_back(getNumLocalVars());
327 
328   // Compute the AffineExpr lower/upper bound for this inequality.
329   SmallVector<int64_t, 8> inequality = getInequality64(ineqPos);
330   SmallVector<int64_t, 8> bound;
331   bound.reserve(getNumCols() - 1);
332   // Everything other than the coefficient at `pos`.
333   bound.append(inequality.begin(), inequality.begin() + pos);
334   bound.append(inequality.begin() + pos + 1, inequality.end());
335 
336   if (inequality[pos] > 0)
337     // Lower bound.
338     std::transform(bound.begin(), bound.end(), bound.begin(),
339                    std::negate<int64_t>());
340   else
341     // Upper bound (which is exclusive).
342     bound.back() += 1;
343 
344   // Convert to AffineExpr (tree) form.
345   auto boundExpr = getAffineExprFromFlatForm(bound, numDims - 1, numSyms,
346                                              localExprs, context);
347 
348   // Get the values to bind to this affine expr (all dims and symbols).
349   SmallVector<Value, 4> operands;
350   getValues(0, pos, &operands);
351   SmallVector<Value, 4> trailingOperands;
352   getValues(pos + 1, getNumDimAndSymbolVars(), &trailingOperands);
353   operands.append(trailingOperands.begin(), trailingOperands.end());
354   vmap.reset(AffineMap::get(numDims - 1, numSyms, boundExpr), operands);
355 }
356 
getDomainSet() const357 FlatAffineValueConstraints FlatAffineRelation::getDomainSet() const {
358   FlatAffineValueConstraints domain = *this;
359   // Convert all range variables to local variables.
360   domain.convertToLocal(VarKind::SetDim, getNumDomainDims(),
361                         getNumDomainDims() + getNumRangeDims());
362   return domain;
363 }
364 
getRangeSet() const365 FlatAffineValueConstraints FlatAffineRelation::getRangeSet() const {
366   FlatAffineValueConstraints range = *this;
367   // Convert all domain variables to local variables.
368   range.convertToLocal(VarKind::SetDim, 0, getNumDomainDims());
369   return range;
370 }
371 
compose(const FlatAffineRelation & other)372 void FlatAffineRelation::compose(const FlatAffineRelation &other) {
373   assert(getNumDomainDims() == other.getNumRangeDims() &&
374          "Domain of this and range of other do not match");
375   assert(space.getDomainSpace().isAligned(other.getSpace().getRangeSpace()) &&
376          "Values of domain of this and range of other do not match");
377 
378   FlatAffineRelation rel = other;
379 
380   // Convert `rel` from
381   //    [otherDomain] -> [otherRange]
382   // to
383   //    [otherDomain] -> [otherRange thisRange]
384   // and `this` from
385   //    [thisDomain] -> [thisRange]
386   // to
387   //    [otherDomain thisDomain] -> [thisRange].
388   unsigned removeDims = rel.getNumRangeDims();
389   insertDomainVar(0, rel.getNumDomainDims());
390   rel.appendRangeVar(getNumRangeDims());
391 
392   // Merge symbol and local variables.
393   mergeSymbolVars(rel);
394   mergeLocalVars(rel);
395 
396   // Convert `rel` from [otherDomain] -> [otherRange thisRange] to
397   // [otherDomain] -> [thisRange] by converting first otherRange range vars
398   // to local vars.
399   rel.convertToLocal(VarKind::SetDim, rel.getNumDomainDims(),
400                      rel.getNumDomainDims() + removeDims);
401   // Convert `this` from [otherDomain thisDomain] -> [thisRange] to
402   // [otherDomain] -> [thisRange] by converting last thisDomain domain vars
403   // to local vars.
404   convertToLocal(VarKind::SetDim, getNumDomainDims() - removeDims,
405                  getNumDomainDims());
406 
407   auto thisMaybeValues = getMaybeValues(VarKind::SetDim);
408   auto relMaybeValues = rel.getMaybeValues(VarKind::SetDim);
409 
410   // Add and match domain of `rel` to domain of `this`.
411   for (unsigned i = 0, e = rel.getNumDomainDims(); i < e; ++i)
412     if (relMaybeValues[i].has_value())
413       setValue(i, *relMaybeValues[i]);
414   // Add and match range of `this` to range of `rel`.
415   for (unsigned i = 0, e = getNumRangeDims(); i < e; ++i) {
416     unsigned rangeIdx = rel.getNumDomainDims() + i;
417     if (thisMaybeValues[rangeIdx].has_value())
418       rel.setValue(rangeIdx, *thisMaybeValues[rangeIdx]);
419   }
420 
421   // Append `this` to `rel` and simplify constraints.
422   rel.append(*this);
423   rel.removeRedundantLocalVars();
424 
425   *this = rel;
426 }
427 
inverse()428 void FlatAffineRelation::inverse() {
429   unsigned oldDomain = getNumDomainDims();
430   unsigned oldRange = getNumRangeDims();
431   // Add new range vars.
432   appendRangeVar(oldDomain);
433   // Swap new vars with domain.
434   for (unsigned i = 0; i < oldDomain; ++i)
435     swapVar(i, oldDomain + oldRange + i);
436   // Remove the swapped domain.
437   removeVarRange(0, oldDomain);
438   // Set domain and range as inverse.
439   numDomainDims = oldRange;
440   numRangeDims = oldDomain;
441 }
442 
insertDomainVar(unsigned pos,unsigned num)443 void FlatAffineRelation::insertDomainVar(unsigned pos, unsigned num) {
444   assert(pos <= getNumDomainDims() &&
445          "Var cannot be inserted at invalid position");
446   insertDimVar(pos, num);
447   numDomainDims += num;
448 }
449 
insertRangeVar(unsigned pos,unsigned num)450 void FlatAffineRelation::insertRangeVar(unsigned pos, unsigned num) {
451   assert(pos <= getNumRangeDims() &&
452          "Var cannot be inserted at invalid position");
453   insertDimVar(getNumDomainDims() + pos, num);
454   numRangeDims += num;
455 }
456 
appendDomainVar(unsigned num)457 void FlatAffineRelation::appendDomainVar(unsigned num) {
458   insertDimVar(getNumDomainDims(), num);
459   numDomainDims += num;
460 }
461 
appendRangeVar(unsigned num)462 void FlatAffineRelation::appendRangeVar(unsigned num) {
463   insertDimVar(getNumDimVars(), num);
464   numRangeDims += num;
465 }
466 
removeVarRange(VarKind kind,unsigned varStart,unsigned varLimit)467 void FlatAffineRelation::removeVarRange(VarKind kind, unsigned varStart,
468                                         unsigned varLimit) {
469   assert(varLimit <= getNumVarKind(kind));
470   if (varStart >= varLimit)
471     return;
472 
473   FlatAffineValueConstraints::removeVarRange(kind, varStart, varLimit);
474 
475   // If kind is not SetDim, domain and range don't need to be updated.
476   if (kind != VarKind::SetDim)
477     return;
478 
479   // Compute number of domain and range variables to remove. This is done by
480   // intersecting the range of domain/range vars with range of vars to remove.
481   unsigned intersectDomainLHS = std::min(varLimit, getNumDomainDims());
482   unsigned intersectDomainRHS = varStart;
483   unsigned intersectRangeLHS = std::min(varLimit, getNumDimVars());
484   unsigned intersectRangeRHS = std::max(varStart, getNumDomainDims());
485 
486   if (intersectDomainLHS > intersectDomainRHS)
487     numDomainDims -= intersectDomainLHS - intersectDomainRHS;
488   if (intersectRangeLHS > intersectRangeRHS)
489     numRangeDims -= intersectRangeLHS - intersectRangeRHS;
490 }
491 
getRelationFromMap(AffineMap & map,IntegerRelation & rel)492 LogicalResult mlir::affine::getRelationFromMap(AffineMap &map,
493                                                IntegerRelation &rel) {
494   // Get flattened affine expressions.
495   std::vector<SmallVector<int64_t, 8>> flatExprs;
496   FlatAffineValueConstraints localVarCst;
497   if (failed(getFlattenedAffineExprs(map, &flatExprs, &localVarCst)))
498     return failure();
499 
500   const unsigned oldDimNum = localVarCst.getNumDimVars();
501   const unsigned oldCols = localVarCst.getNumCols();
502   const unsigned numRangeVars = map.getNumResults();
503   const unsigned numDomainVars = map.getNumDims();
504 
505   // Add range as the new expressions.
506   localVarCst.appendDimVar(numRangeVars);
507 
508   // Add identifiers to the local constraints as getFlattenedAffineExprs creates
509   // a FlatLinearConstraints with no identifiers.
510   for (unsigned i = 0, e = localVarCst.getNumDimAndSymbolVars(); i < e; ++i)
511     localVarCst.setValue(i, Value());
512 
513   // Add equalities between source and range.
514   SmallVector<int64_t, 8> eq(localVarCst.getNumCols());
515   for (unsigned i = 0, e = map.getNumResults(); i < e; ++i) {
516     // Zero fill.
517     std::fill(eq.begin(), eq.end(), 0);
518     // Fill equality.
519     for (unsigned j = 0, f = oldDimNum; j < f; ++j)
520       eq[j] = flatExprs[i][j];
521     for (unsigned j = oldDimNum, f = oldCols; j < f; ++j)
522       eq[j + numRangeVars] = flatExprs[i][j];
523     // Set this dimension to -1 to equate lhs and rhs and add equality.
524     eq[numDomainVars + i] = -1;
525     localVarCst.addEquality(eq);
526   }
527 
528   rel = localVarCst;
529   return success();
530 }
531 
getRelationFromMap(const AffineValueMap & map,IntegerRelation & rel)532 LogicalResult mlir::affine::getRelationFromMap(const AffineValueMap &map,
533                                                IntegerRelation &rel) {
534 
535   AffineMap affineMap = map.getAffineMap();
536   if (failed(getRelationFromMap(affineMap, rel)))
537     return failure();
538 
539   // Set identifiers for domain and symbol variables.
540   for (unsigned i = 0, e = affineMap.getNumDims(); i < e; ++i)
541     rel.setId(VarKind::SetDim, i, Identifier(map.getOperand(i)));
542 
543   const unsigned mapNumResults = affineMap.getNumResults();
544   for (unsigned i = 0, e = rel.getNumSymbolVars(); i < e; ++i)
545     rel.setId(
546         VarKind::Symbol, i,
547         Identifier(map.getOperand(rel.getNumDimVars() + i - mapNumResults)));
548 
549   return success();
550 }
551