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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

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