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