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

source code of mlir/lib/Dialect/Affine/Analysis/AffineStructures.cpp