MLIR  17.0.0git
AffineMap.cpp
Go to the documentation of this file.
1 //===- AffineMap.cpp - MLIR Affine Map Classes ----------------------------===//
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 #include "mlir/IR/AffineMap.h"
10 #include "AffineMapDetail.h"
11 #include "mlir/IR/AffineExpr.h"
13 #include "mlir/IR/BuiltinTypes.h"
16 #include "llvm/ADT/STLExtras.h"
17 #include "llvm/ADT/SmallBitVector.h"
18 #include "llvm/ADT/SmallSet.h"
19 #include "llvm/ADT/SmallVector.h"
20 #include "llvm/ADT/StringRef.h"
21 #include "llvm/Support/raw_ostream.h"
22 #include <iterator>
23 #include <numeric>
24 #include <optional>
25 #include <type_traits>
26 
27 using namespace mlir;
28 
29 namespace {
30 
31 // AffineExprConstantFolder evaluates an affine expression using constant
32 // operands passed in 'operandConsts'. Returns an IntegerAttr attribute
33 // representing the constant value of the affine expression evaluated on
34 // constant 'operandConsts', or nullptr if it can't be folded.
35 class AffineExprConstantFolder {
36 public:
37  AffineExprConstantFolder(unsigned numDims, ArrayRef<Attribute> operandConsts)
38  : numDims(numDims), operandConsts(operandConsts) {}
39 
40  /// Attempt to constant fold the specified affine expr, or return null on
41  /// failure.
42  IntegerAttr constantFold(AffineExpr expr) {
43  if (auto result = constantFoldImpl(expr))
44  return IntegerAttr::get(IndexType::get(expr.getContext()), *result);
45  return nullptr;
46  }
47 
48 private:
49  std::optional<int64_t> constantFoldImpl(AffineExpr expr) {
50  switch (expr.getKind()) {
52  return constantFoldBinExpr(
53  expr, [](int64_t lhs, int64_t rhs) { return lhs + rhs; });
55  return constantFoldBinExpr(
56  expr, [](int64_t lhs, int64_t rhs) { return lhs * rhs; });
58  return constantFoldBinExpr(
59  expr, [](int64_t lhs, int64_t rhs) { return mod(lhs, rhs); });
61  return constantFoldBinExpr(
62  expr, [](int64_t lhs, int64_t rhs) { return floorDiv(lhs, rhs); });
64  return constantFoldBinExpr(
65  expr, [](int64_t lhs, int64_t rhs) { return ceilDiv(lhs, rhs); });
67  return expr.cast<AffineConstantExpr>().getValue();
69  if (auto attr = llvm::dyn_cast_or_null<IntegerAttr>(
70  operandConsts[expr.cast<AffineDimExpr>().getPosition()]))
71  return attr.getInt();
72  return std::nullopt;
74  if (auto attr = llvm::dyn_cast_or_null<IntegerAttr>(
75  operandConsts[numDims +
76  expr.cast<AffineSymbolExpr>().getPosition()]))
77  return attr.getInt();
78  return std::nullopt;
79  }
80  llvm_unreachable("Unknown AffineExpr");
81  }
82 
83  // TODO: Change these to operate on APInts too.
84  std::optional<int64_t> constantFoldBinExpr(AffineExpr expr,
85  int64_t (*op)(int64_t, int64_t)) {
86  auto binOpExpr = expr.cast<AffineBinaryOpExpr>();
87  if (auto lhs = constantFoldImpl(binOpExpr.getLHS()))
88  if (auto rhs = constantFoldImpl(binOpExpr.getRHS()))
89  return op(*lhs, *rhs);
90  return std::nullopt;
91  }
92 
93  // The number of dimension operands in AffineMap containing this expression.
94  unsigned numDims;
95  // The constant valued operands used to evaluate this AffineExpr.
96  ArrayRef<Attribute> operandConsts;
97 };
98 
99 } // namespace
100 
101 /// Returns a single constant result affine map.
103  return get(/*dimCount=*/0, /*symbolCount=*/0,
104  {getAffineConstantExpr(val, context)});
105 }
106 
107 /// Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most
108 /// minor dimensions.
109 AffineMap AffineMap::getMinorIdentityMap(unsigned dims, unsigned results,
110  MLIRContext *context) {
111  assert(dims >= results && "Dimension mismatch");
112  auto id = AffineMap::getMultiDimIdentityMap(dims, context);
113  return AffineMap::get(dims, 0, id.getResults().take_back(results), context);
114 }
115 
117  return getNumDims() >= getNumResults() &&
118  *this ==
120 }
121 
122 /// Returns true if this affine map is a minor identity up to broadcasted
123 /// dimensions which are indicated by value 0 in the result.
125  SmallVectorImpl<unsigned> *broadcastedDims) const {
126  if (broadcastedDims)
127  broadcastedDims->clear();
128  if (getNumDims() < getNumResults())
129  return false;
130  unsigned suffixStart = getNumDims() - getNumResults();
131  for (const auto &idxAndExpr : llvm::enumerate(getResults())) {
132  unsigned resIdx = idxAndExpr.index();
133  AffineExpr expr = idxAndExpr.value();
134  if (auto constExpr = expr.dyn_cast<AffineConstantExpr>()) {
135  // Each result may be either a constant 0 (broadcasted dimension).
136  if (constExpr.getValue() != 0)
137  return false;
138  if (broadcastedDims)
139  broadcastedDims->push_back(resIdx);
140  } else if (auto dimExpr = expr.dyn_cast<AffineDimExpr>()) {
141  // Or it may be the input dimension corresponding to this result position.
142  if (dimExpr.getPosition() != suffixStart + resIdx)
143  return false;
144  } else {
145  return false;
146  }
147  }
148  return true;
149 }
150 
151 /// Return true if this affine map can be converted to a minor identity with
152 /// broadcast by doing a permute. Return a permutation (there may be
153 /// several) to apply to get to a minor identity with broadcasts.
154 /// Ex:
155 /// * (d0, d1, d2) -> (0, d1) maps to minor identity (d1, 0 = d2) with
156 /// perm = [1, 0] and broadcast d2
157 /// * (d0, d1, d2) -> (d0, 0) cannot be mapped to a minor identity by
158 /// permutation + broadcast
159 /// * (d0, d1, d2, d3) -> (0, d1, d3) maps to minor identity (d1, 0 = d2, d3)
160 /// with perm = [1, 0, 2] and broadcast d2
161 /// * (d0, d1) -> (d1, 0, 0, d0) maps to minor identity (d0, d1) with extra
162 /// leading broadcat dimensions. The map returned would be (0, 0, d0, d1) with
163 /// perm = [3, 0, 1, 2]
165  SmallVectorImpl<unsigned> &permutedDims) const {
166  unsigned projectionStart =
168  permutedDims.clear();
169  SmallVector<unsigned> broadcastDims;
170  permutedDims.resize(getNumResults(), 0);
171  // If there are more results than input dimensions we want the new map to
172  // start with broadcast dimensions in order to be a minor identity with
173  // broadcasting.
174  unsigned leadingBroadcast =
176  llvm::SmallBitVector dimFound(std::max(getNumInputs(), getNumResults()),
177  false);
178  for (const auto &idxAndExpr : llvm::enumerate(getResults())) {
179  unsigned resIdx = idxAndExpr.index();
180  AffineExpr expr = idxAndExpr.value();
181  // Each result may be either a constant 0 (broadcast dimension) or a
182  // dimension.
183  if (auto constExpr = expr.dyn_cast<AffineConstantExpr>()) {
184  if (constExpr.getValue() != 0)
185  return false;
186  broadcastDims.push_back(resIdx);
187  } else if (auto dimExpr = expr.dyn_cast<AffineDimExpr>()) {
188  if (dimExpr.getPosition() < projectionStart)
189  return false;
190  unsigned newPosition =
191  dimExpr.getPosition() - projectionStart + leadingBroadcast;
192  permutedDims[resIdx] = newPosition;
193  dimFound[newPosition] = true;
194  } else {
195  return false;
196  }
197  }
198  // Find a permuation for the broadcast dimension. Since they are broadcasted
199  // any valid permutation is acceptable. We just permute the dim into a slot
200  // without an existing dimension.
201  unsigned pos = 0;
202  for (auto dim : broadcastDims) {
203  while (pos < dimFound.size() && dimFound[pos]) {
204  pos++;
205  }
206  permutedDims[dim] = pos++;
207  }
208  return true;
209 }
210 
211 /// Returns an AffineMap representing a permutation.
213  MLIRContext *context) {
214  assert(!permutation.empty() &&
215  "Cannot create permutation map from empty permutation vector");
217  for (auto index : permutation)
218  affExprs.push_back(getAffineDimExpr(index, context));
219  const auto *m = std::max_element(permutation.begin(), permutation.end());
220  auto permutationMap = AffineMap::get(*m + 1, 0, affExprs, context);
221  assert(permutationMap.isPermutation() && "Invalid permutation vector");
222  return permutationMap;
223 }
224 
225 template <typename AffineExprContainer>
228  assert(!exprsList.empty());
229  assert(!exprsList[0].empty());
230  auto context = exprsList[0][0].getContext();
231  int64_t maxDim = -1, maxSym = -1;
232  getMaxDimAndSymbol(exprsList, maxDim, maxSym);
234  maps.reserve(exprsList.size());
235  for (const auto &exprs : exprsList)
236  maps.push_back(AffineMap::get(/*dimCount=*/maxDim + 1,
237  /*symbolCount=*/maxSym + 1, exprs, context));
238  return maps;
239 }
240 
243  return ::inferFromExprList(exprsList);
244 }
245 
248  return ::inferFromExprList(exprsList);
249 }
250 
252  uint64_t gcd = 0;
253  for (AffineExpr resultExpr : getResults()) {
254  uint64_t thisGcd = resultExpr.getLargestKnownDivisor();
255  gcd = std::gcd(gcd, thisGcd);
256  }
257  if (gcd == 0)
259  return gcd;
260 }
261 
263  MLIRContext *context) {
265  dimExprs.reserve(numDims);
266  for (unsigned i = 0; i < numDims; ++i)
267  dimExprs.push_back(mlir::getAffineDimExpr(i, context));
268  return get(/*dimCount=*/numDims, /*symbolCount=*/0, dimExprs, context);
269 }
270 
271 MLIRContext *AffineMap::getContext() const { return map->context; }
272 
273 bool AffineMap::isIdentity() const {
274  if (getNumDims() != getNumResults())
275  return false;
276  ArrayRef<AffineExpr> results = getResults();
277  for (unsigned i = 0, numDims = getNumDims(); i < numDims; ++i) {
278  auto expr = results[i].dyn_cast<AffineDimExpr>();
279  if (!expr || expr.getPosition() != i)
280  return false;
281  }
282  return true;
283 }
284 
286  if (getNumSymbols() != getNumResults())
287  return false;
288  ArrayRef<AffineExpr> results = getResults();
289  for (unsigned i = 0, numSymbols = getNumSymbols(); i < numSymbols; ++i) {
290  auto expr = results[i].dyn_cast<AffineDimExpr>();
291  if (!expr || expr.getPosition() != i)
292  return false;
293  }
294  return true;
295 }
296 
297 bool AffineMap::isEmpty() const {
298  return getNumDims() == 0 && getNumSymbols() == 0 && getNumResults() == 0;
299 }
300 
302  return getNumResults() == 1 && getResult(0).isa<AffineConstantExpr>();
303 }
304 
305 bool AffineMap::isConstant() const {
306  return llvm::all_of(getResults(), [](AffineExpr expr) {
307  return expr.isa<AffineConstantExpr>();
308  });
309 }
310 
312  assert(isSingleConstant() && "map must have a single constant result");
313  return getResult(0).cast<AffineConstantExpr>().getValue();
314 }
315 
317  assert(isConstant() && "map must have only constant results");
318  SmallVector<int64_t> result;
319  for (auto expr : getResults())
320  result.emplace_back(expr.cast<AffineConstantExpr>().getValue());
321  return result;
322 }
323 
324 unsigned AffineMap::getNumDims() const {
325  assert(map && "uninitialized map storage");
326  return map->numDims;
327 }
328 unsigned AffineMap::getNumSymbols() const {
329  assert(map && "uninitialized map storage");
330  return map->numSymbols;
331 }
332 unsigned AffineMap::getNumResults() const { return getResults().size(); }
333 unsigned AffineMap::getNumInputs() const {
334  assert(map && "uninitialized map storage");
335  return map->numDims + map->numSymbols;
336 }
338  assert(map && "uninitialized map storage");
339  return map->results();
340 }
341 AffineExpr AffineMap::getResult(unsigned idx) const {
342  return getResults()[idx];
343 }
344 
345 unsigned AffineMap::getDimPosition(unsigned idx) const {
346  return getResult(idx).cast<AffineDimExpr>().getPosition();
347 }
348 
349 std::optional<unsigned> AffineMap::getResultPosition(AffineExpr input) const {
350  if (!input.isa<AffineDimExpr>())
351  return std::nullopt;
352 
353  for (unsigned i = 0, numResults = getNumResults(); i < numResults; i++) {
354  if (getResult(i) == input)
355  return i;
356  }
357 
358  return std::nullopt;
359 }
360 
361 /// Folds the results of the application of an affine map on the provided
362 /// operands to a constant if possible. Returns false if the folding happens,
363 /// true otherwise.
366  SmallVectorImpl<Attribute> &results) const {
367  // Attempt partial folding.
368  SmallVector<int64_t, 2> integers;
369  partialConstantFold(operandConstants, &integers);
370 
371  // If all expressions folded to a constant, populate results with attributes
372  // containing those constants.
373  if (integers.empty())
374  return failure();
375 
376  auto range = llvm::map_range(integers, [this](int64_t i) {
378  });
379  results.append(range.begin(), range.end());
380  return success();
381 }
382 
383 AffineMap
385  SmallVectorImpl<int64_t> *results) const {
386  assert(getNumInputs() == operandConstants.size());
387 
388  // Fold each of the result expressions.
389  AffineExprConstantFolder exprFolder(getNumDims(), operandConstants);
391  exprs.reserve(getNumResults());
392 
393  for (auto expr : getResults()) {
394  auto folded = exprFolder.constantFold(expr);
395  // If did not fold to a constant, keep the original expression, and clear
396  // the integer results vector.
397  if (folded) {
398  exprs.push_back(
399  getAffineConstantExpr(folded.getInt(), folded.getContext()));
400  if (results)
401  results->push_back(folded.getInt());
402  } else {
403  exprs.push_back(expr);
404  if (results) {
405  results->clear();
406  results = nullptr;
407  }
408  }
409  }
410 
411  return get(getNumDims(), getNumSymbols(), exprs, getContext());
412 }
413 
414 /// Walk all of the AffineExpr's in this mapping. Each node in an expression
415 /// tree is visited in postorder.
417  for (auto expr : getResults())
418  expr.walk(callback);
419 }
420 
421 /// This method substitutes any uses of dimensions and symbols (e.g.
422 /// dim#0 with dimReplacements[0]) in subexpressions and returns the modified
423 /// expression mapping. Because this can be used to eliminate dims and
424 /// symbols, the client needs to specify the number of dims and symbols in
425 /// the result. The returned map always has the same number of results.
427  ArrayRef<AffineExpr> symReplacements,
428  unsigned numResultDims,
429  unsigned numResultSyms) const {
431  results.reserve(getNumResults());
432  for (auto expr : getResults())
433  results.push_back(
434  expr.replaceDimsAndSymbols(dimReplacements, symReplacements));
435  return get(numResultDims, numResultSyms, results, getContext());
436 }
437 
438 /// Sparse replace method. Apply AffineExpr::replace(`expr`, `replacement`) to
439 /// each of the results and return a new AffineMap with the new results and
440 /// with the specified number of dims and symbols.
442  unsigned numResultDims,
443  unsigned numResultSyms) const {
444  SmallVector<AffineExpr, 4> newResults;
445  newResults.reserve(getNumResults());
446  for (AffineExpr e : getResults())
447  newResults.push_back(e.replace(expr, replacement));
448  return AffineMap::get(numResultDims, numResultSyms, newResults, getContext());
449 }
450 
451 /// Sparse replace method. Apply AffineExpr::replace(`map`) to each of the
452 /// results and return a new AffineMap with the new results and with the
453 /// specified number of dims and symbols.
455  unsigned numResultDims,
456  unsigned numResultSyms) const {
457  SmallVector<AffineExpr, 4> newResults;
458  newResults.reserve(getNumResults());
459  for (AffineExpr e : getResults())
460  newResults.push_back(e.replace(map));
461  return AffineMap::get(numResultDims, numResultSyms, newResults, getContext());
462 }
463 
464 AffineMap
466  SmallVector<AffineExpr, 4> newResults;
467  newResults.reserve(getNumResults());
468  for (AffineExpr e : getResults())
469  newResults.push_back(e.replace(map));
470  return AffineMap::inferFromExprList(newResults).front();
471 }
472 
473 AffineMap AffineMap::dropResults(const llvm::SmallBitVector &positions) const {
474  auto exprs = llvm::to_vector<4>(getResults());
475  // TODO: this is a pretty terrible API .. is there anything better?
476  for (auto pos = positions.find_last(); pos != -1;
477  pos = positions.find_prev(pos))
478  exprs.erase(exprs.begin() + pos);
479  return AffineMap::get(getNumDims(), getNumSymbols(), exprs, getContext());
480 }
481 
483  assert(getNumDims() == map.getNumResults() && "Number of results mismatch");
484  // Prepare `map` by concatenating the symbols and rewriting its exprs.
485  unsigned numDims = map.getNumDims();
486  unsigned numSymbolsThisMap = getNumSymbols();
487  unsigned numSymbols = numSymbolsThisMap + map.getNumSymbols();
488  SmallVector<AffineExpr, 8> newDims(numDims);
489  for (unsigned idx = 0; idx < numDims; ++idx) {
490  newDims[idx] = getAffineDimExpr(idx, getContext());
491  }
492  SmallVector<AffineExpr, 8> newSymbols(numSymbols - numSymbolsThisMap);
493  for (unsigned idx = numSymbolsThisMap; idx < numSymbols; ++idx) {
494  newSymbols[idx - numSymbolsThisMap] =
496  }
497  auto newMap =
498  map.replaceDimsAndSymbols(newDims, newSymbols, numDims, numSymbols);
500  exprs.reserve(getResults().size());
501  for (auto expr : getResults())
502  exprs.push_back(expr.compose(newMap));
503  return AffineMap::get(numDims, numSymbols, exprs, map.getContext());
504 }
505 
507  assert(getNumSymbols() == 0 && "Expected symbol-less map");
509  exprs.reserve(values.size());
510  MLIRContext *ctx = getContext();
511  for (auto v : values)
512  exprs.push_back(getAffineConstantExpr(v, ctx));
513  auto resMap = compose(AffineMap::get(0, 0, exprs, ctx));
515  res.reserve(resMap.getNumResults());
516  for (auto e : resMap.getResults())
517  res.push_back(e.cast<AffineConstantExpr>().getValue());
518  return res;
519 }
520 
521 bool AffineMap::isProjectedPermutation(bool allowZeroInResults) const {
522  if (getNumSymbols() > 0)
523  return false;
524 
525  // Having more results than inputs means that results have duplicated dims or
526  // zeros that can't be mapped to input dims.
527  if (getNumResults() > getNumInputs())
528  return false;
529 
530  SmallVector<bool, 8> seen(getNumInputs(), false);
531  // A projected permutation can have, at most, only one instance of each input
532  // dimension in the result expressions. Zeros are allowed as long as the
533  // number of result expressions is lower or equal than the number of input
534  // expressions.
535  for (auto expr : getResults()) {
536  if (auto dim = expr.dyn_cast<AffineDimExpr>()) {
537  if (seen[dim.getPosition()])
538  return false;
539  seen[dim.getPosition()] = true;
540  } else {
541  auto constExpr = expr.dyn_cast<AffineConstantExpr>();
542  if (!allowZeroInResults || !constExpr || constExpr.getValue() != 0)
543  return false;
544  }
545  }
546 
547  // Results are either dims or zeros and zeros can be mapped to input dims.
548  return true;
549 }
550 
552  if (getNumDims() != getNumResults())
553  return false;
554  return isProjectedPermutation();
555 }
556 
559  exprs.reserve(resultPos.size());
560  for (auto idx : resultPos)
561  exprs.push_back(getResult(idx));
562  return AffineMap::get(getNumDims(), getNumSymbols(), exprs, getContext());
563 }
564 
565 AffineMap AffineMap::getSliceMap(unsigned start, unsigned length) const {
567  getResults().slice(start, length), getContext());
568 }
569 
570 AffineMap AffineMap::getMajorSubMap(unsigned numResults) const {
571  if (numResults == 0)
572  return AffineMap();
573  if (numResults > getNumResults())
574  return *this;
575  return getSliceMap(0, numResults);
576 }
577 
578 AffineMap AffineMap::getMinorSubMap(unsigned numResults) const {
579  if (numResults == 0)
580  return AffineMap();
581  if (numResults > getNumResults())
582  return *this;
583  return getSliceMap(getNumResults() - numResults, numResults);
584 }
585 
586 /// Implementation detail to compress multiple affine maps with a compressionFun
587 /// that is expected to be either compressUnusedDims or compressUnusedSymbols.
588 /// The implementation keeps track of num dims and symbols across the different
589 /// affine maps.
591  ArrayRef<AffineMap> maps,
592  llvm::function_ref<AffineMap(AffineMap)> compressionFun) {
593  if (maps.empty())
594  return SmallVector<AffineMap>();
595  SmallVector<AffineExpr> allExprs;
596  allExprs.reserve(maps.size() * maps.front().getNumResults());
597  unsigned numDims = maps.front().getNumDims(),
598  numSymbols = maps.front().getNumSymbols();
599  for (auto m : maps) {
600  assert(numDims == m.getNumDims() && numSymbols == m.getNumSymbols() &&
601  "expected maps with same num dims and symbols");
602  llvm::append_range(allExprs, m.getResults());
603  }
604  AffineMap unifiedMap = compressionFun(
605  AffineMap::get(numDims, numSymbols, allExprs, maps.front().getContext()));
606  unsigned unifiedNumDims = unifiedMap.getNumDims(),
607  unifiedNumSymbols = unifiedMap.getNumSymbols();
608  ArrayRef<AffineExpr> unifiedResults = unifiedMap.getResults();
610  res.reserve(maps.size());
611  for (auto m : maps) {
612  res.push_back(AffineMap::get(unifiedNumDims, unifiedNumSymbols,
613  unifiedResults.take_front(m.getNumResults()),
614  m.getContext()));
615  unifiedResults = unifiedResults.drop_front(m.getNumResults());
616  }
617  return res;
618 }
619 
621  const llvm::SmallBitVector &unusedDims) {
622  return projectDims(map, unusedDims, /*compressDimsFlag=*/true);
623 }
624 
626  return compressDims(map, getUnusedDimsBitVector({map}));
627 }
628 
630  return compressUnusedListImpl(
631  maps, [](AffineMap m) { return compressUnusedDims(m); });
632 }
633 
635  const llvm::SmallBitVector &unusedSymbols) {
636  return projectSymbols(map, unusedSymbols, /*compressSymbolsFlag=*/true);
637 }
638 
640  return compressSymbols(map, getUnusedSymbolsBitVector({map}));
641 }
642 
644  return compressUnusedListImpl(
645  maps, [](AffineMap m) { return compressUnusedSymbols(m); });
646 }
647 
650  for (auto e : map.getResults()) {
651  exprs.push_back(
652  simplifyAffineExpr(e, map.getNumDims(), map.getNumSymbols()));
653  }
654  return AffineMap::get(map.getNumDims(), map.getNumSymbols(), exprs,
655  map.getContext());
656 }
657 
659  auto results = map.getResults();
660  SmallVector<AffineExpr, 4> uniqueExprs(results.begin(), results.end());
661  uniqueExprs.erase(std::unique(uniqueExprs.begin(), uniqueExprs.end()),
662  uniqueExprs.end());
663  return AffineMap::get(map.getNumDims(), map.getNumSymbols(), uniqueExprs,
664  map.getContext());
665 }
666 
668  if (map.isEmpty())
669  return map;
670  assert(map.getNumSymbols() == 0 && "expected map without symbols");
672  for (const auto &en : llvm::enumerate(map.getResults())) {
673  auto expr = en.value();
674  // Skip non-permutations.
675  if (auto d = expr.dyn_cast<AffineDimExpr>()) {
676  if (exprs[d.getPosition()])
677  continue;
678  exprs[d.getPosition()] = getAffineDimExpr(en.index(), d.getContext());
679  }
680  }
681  SmallVector<AffineExpr, 4> seenExprs;
682  seenExprs.reserve(map.getNumDims());
683  for (auto expr : exprs)
684  if (expr)
685  seenExprs.push_back(expr);
686  if (seenExprs.size() != map.getNumInputs())
687  return AffineMap();
688  return AffineMap::get(map.getNumResults(), 0, seenExprs, map.getContext());
689 }
690 
692  assert(map.isProjectedPermutation(/*allowZeroInResults=*/true));
693  MLIRContext *context = map.getContext();
694  AffineExpr zero = mlir::getAffineConstantExpr(0, context);
695  // Start with all the results as 0.
696  SmallVector<AffineExpr, 4> exprs(map.getNumInputs(), zero);
697  for (unsigned i : llvm::seq(unsigned(0), map.getNumResults())) {
698  // Skip zeros from input map. 'exprs' is already initialized to zero.
699  if (auto constExpr = map.getResult(i).dyn_cast<AffineConstantExpr>()) {
700  assert(constExpr.getValue() == 0 &&
701  "Unexpected constant in projected permutation");
702  (void)constExpr;
703  continue;
704  }
705 
706  // Reverse each dimension existing in the original map result.
707  exprs[map.getDimPosition(i)] = getAffineDimExpr(i, context);
708  }
709  return AffineMap::get(map.getNumResults(), /*symbolCount=*/0, exprs, context);
710 }
711 
713  unsigned numResults = 0, numDims = 0, numSymbols = 0;
714  for (auto m : maps)
715  numResults += m.getNumResults();
717  results.reserve(numResults);
718  for (auto m : maps) {
719  for (auto res : m.getResults())
720  results.push_back(res.shiftSymbols(m.getNumSymbols(), numSymbols));
721 
722  numSymbols += m.getNumSymbols();
723  numDims = std::max(m.getNumDims(), numDims);
724  }
725  return AffineMap::get(numDims, numSymbols, results,
726  maps.front().getContext());
727 }
728 
729 /// Common implementation to project out dimensions or symbols from an affine
730 /// map based on the template type.
731 /// Additionally, if 'compress' is true, the projected out dimensions or symbols
732 /// are also dropped from the resulting map.
733 template <typename AffineDimOrSymExpr>
735  const llvm::SmallBitVector &toProject,
736  bool compress) {
737  static_assert(llvm::is_one_of<AffineDimOrSymExpr, AffineDimExpr,
738  AffineSymbolExpr>::value,
739  "expected AffineDimExpr or AffineSymbolExpr");
740 
741  constexpr bool isDim = std::is_same<AffineDimOrSymExpr, AffineDimExpr>::value;
742  int64_t numDimOrSym = (isDim) ? map.getNumDims() : map.getNumSymbols();
743  SmallVector<AffineExpr> replacements;
744  replacements.reserve(numDimOrSym);
745 
746  auto createNewDimOrSym = (isDim) ? getAffineDimExpr : getAffineSymbolExpr;
747 
748  using replace_fn_ty =
749  std::function<AffineExpr(AffineExpr, ArrayRef<AffineExpr>)>;
750  replace_fn_ty replaceDims = [](AffineExpr e,
751  ArrayRef<AffineExpr> replacements) {
752  return e.replaceDims(replacements);
753  };
754  replace_fn_ty replaceSymbols = [](AffineExpr e,
755  ArrayRef<AffineExpr> replacements) {
756  return e.replaceSymbols(replacements);
757  };
758  replace_fn_ty replaceNewDimOrSym = (isDim) ? replaceDims : replaceSymbols;
759 
760  MLIRContext *context = map.getContext();
761  int64_t newNumDimOrSym = 0;
762  for (unsigned dimOrSym = 0; dimOrSym < numDimOrSym; ++dimOrSym) {
763  if (toProject.test(dimOrSym)) {
764  replacements.push_back(getAffineConstantExpr(0, context));
765  continue;
766  }
767  int64_t newPos = compress ? newNumDimOrSym++ : dimOrSym;
768  replacements.push_back(createNewDimOrSym(newPos, context));
769  }
770  SmallVector<AffineExpr> resultExprs;
771  resultExprs.reserve(map.getNumResults());
772  for (auto e : map.getResults())
773  resultExprs.push_back(replaceNewDimOrSym(e, replacements));
774 
775  int64_t numDims = (compress && isDim) ? newNumDimOrSym : map.getNumDims();
776  int64_t numSyms = (compress && !isDim) ? newNumDimOrSym : map.getNumSymbols();
777  return AffineMap::get(numDims, numSyms, resultExprs, context);
778 }
779 
781  const llvm::SmallBitVector &projectedDimensions,
782  bool compressDimsFlag) {
783  return projectCommonImpl<AffineDimExpr>(map, projectedDimensions,
784  compressDimsFlag);
785 }
786 
788  const llvm::SmallBitVector &projectedSymbols,
789  bool compressSymbolsFlag) {
790  return projectCommonImpl<AffineSymbolExpr>(map, projectedSymbols,
791  compressSymbolsFlag);
792 }
793 
795  const llvm::SmallBitVector &projectedDimensions,
796  bool compressDimsFlag,
797  bool compressSymbolsFlag) {
798  map = projectDims(map, projectedDimensions, compressDimsFlag);
799  if (compressSymbolsFlag)
800  map = compressUnusedSymbols(map);
801  return map;
802 }
803 
805  unsigned numDims = maps[0].getNumDims();
806  llvm::SmallBitVector numDimsBitVector(numDims, true);
807  for (AffineMap m : maps) {
808  for (unsigned i = 0; i < numDims; ++i) {
809  if (m.isFunctionOfDim(i))
810  numDimsBitVector.reset(i);
811  }
812  }
813  return numDimsBitVector;
814 }
815 
817  unsigned numSymbols = maps[0].getNumSymbols();
818  llvm::SmallBitVector numSymbolsBitVector(numSymbols, true);
819  for (AffineMap m : maps) {
820  for (unsigned i = 0; i < numSymbols; ++i) {
821  if (m.isFunctionOfSymbol(i))
822  numSymbolsBitVector.reset(i);
823  }
824  }
825  return numSymbolsBitVector;
826 }
827 
828 AffineMap
830  const llvm::SmallBitVector &projectedDimensions) {
831  auto id = AffineMap::getMultiDimIdentityMap(rank, map.getContext());
832  AffineMap proj = id.dropResults(projectedDimensions);
833  return map.compose(proj);
834 }
835 
836 //===----------------------------------------------------------------------===//
837 // MutableAffineMap.
838 //===----------------------------------------------------------------------===//
839 
841  : results(map.getResults().begin(), map.getResults().end()),
842  numDims(map.getNumDims()), numSymbols(map.getNumSymbols()),
843  context(map.getContext()) {}
844 
846  results.clear();
847  numDims = map.getNumDims();
848  numSymbols = map.getNumSymbols();
849  context = map.getContext();
850  llvm::append_range(results, map.getResults());
851 }
852 
853 bool MutableAffineMap::isMultipleOf(unsigned idx, int64_t factor) const {
854  if (results[idx].isMultipleOf(factor))
855  return true;
856 
857  // TODO: use simplifyAffineExpr and FlatAffineValueConstraints to
858  // complete this (for a more powerful analysis).
859  return false;
860 }
861 
862 // Simplifies the result affine expressions of this map. The expressions
863 // have to be pure for the simplification implemented.
865  // Simplify each of the results if possible.
866  // TODO: functional-style map
867  for (unsigned i = 0, e = getNumResults(); i < e; i++) {
868  results[i] = simplifyAffineExpr(getResult(i), numDims, numSymbols);
869  }
870 }
871 
873  return AffineMap::get(numDims, numSymbols, results, context);
874 }
static SmallVector< AffineMap > compressUnusedListImpl(ArrayRef< AffineMap > maps, llvm::function_ref< AffineMap(AffineMap)> compressionFun)
Implementation detail to compress multiple affine maps with a compressionFun that is expected to be e...
Definition: AffineMap.cpp:590
static SmallVector< AffineMap, 4 > inferFromExprList(ArrayRef< AffineExprContainer > exprsList)
Definition: AffineMap.cpp:227
static AffineMap projectCommonImpl(AffineMap map, const llvm::SmallBitVector &toProject, bool compress)
Common implementation to project out dimensions or symbols from an affine map based on the template t...
Definition: AffineMap.cpp:734
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
Affine binary operation expression.
Definition: AffineExpr.h:207
An integer constant appearing in affine expression.
Definition: AffineExpr.h:232
int64_t getValue() const
Definition: AffineExpr.cpp:519
A dimensional identifier appearing in an affine expression.
Definition: AffineExpr.h:216
unsigned getPosition() const
Definition: AffineExpr.cpp:325
Base type for affine expression.
Definition: AffineExpr.h:68
AffineExpr replaceDimsAndSymbols(ArrayRef< AffineExpr > dimReplacements, ArrayRef< AffineExpr > symReplacements) const
This method substitutes any uses of dimensions and symbols (e.g.
Definition: AffineExpr.cpp:66
U cast() const
Definition: AffineExpr.h:291
void walk(std::function< void(AffineExpr)> callback) const
Walk all of the AffineExpr's in this expression in postorder.
Definition: AffineExpr.cpp:30
AffineExprKind getKind() const
Return the classification for this type.
Definition: AffineExpr.cpp:27
AffineExpr compose(AffineMap map) const
Compose with an AffineMap.
Definition: AffineExpr.cpp:889
constexpr bool isa() const
Definition: AffineExpr.h:270
AffineExpr replaceDims(ArrayRef< AffineExpr > dimReplacements) const
Dim-only version of replaceDimsAndSymbols.
Definition: AffineExpr.cpp:99
MLIRContext * getContext() const
Definition: AffineExpr.cpp:25
AffineExpr replaceSymbols(ArrayRef< AffineExpr > symReplacements) const
Symbol-only version of replaceDimsAndSymbols.
Definition: AffineExpr.cpp:104
U dyn_cast() const
Definition: AffineExpr.h:281
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
Definition: AffineMap.h:44
int64_t getSingleConstantResult() const
Returns the constant result of this map.
Definition: AffineMap.cpp:311
static AffineMap getMinorIdentityMap(unsigned dims, unsigned results, MLIRContext *context)
Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most minor dimensions.
Definition: AffineMap.cpp:109
AffineMap dropResults(ArrayRef< int64_t > positions) const
Definition: AffineMap.h:259
AffineMap getSliceMap(unsigned start, unsigned length) const
Returns the map consisting of length expressions starting from start.
Definition: AffineMap.cpp:565
AffineMap getMajorSubMap(unsigned numResults) const
Returns the map consisting of the most major numResults results.
Definition: AffineMap.cpp:570
MLIRContext * getContext() const
Definition: AffineMap.cpp:271
AffineMap partialConstantFold(ArrayRef< Attribute > operandConstants, SmallVectorImpl< int64_t > *results=nullptr) const
Propagates the constant operands into this affine map.
Definition: AffineMap.cpp:384
bool isMinorIdentity() const
Returns true if this affine map is a minor identity, i.e.
Definition: AffineMap.cpp:116
unsigned getDimPosition(unsigned idx) const
Extracts the position of the dimensional expression at the given result, when the caller knows it is ...
Definition: AffineMap.cpp:345
bool isConstant() const
Returns true if this affine map has only constant results.
Definition: AffineMap.cpp:305
static AffineMap getMultiDimIdentityMap(unsigned numDims, MLIRContext *context)
Returns an AffineMap with 'numDims' identity result dim exprs.
Definition: AffineMap.cpp:262
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
bool isSingleConstant() const
Returns true if this affine map is a single result constant function.
Definition: AffineMap.cpp:301
bool isProjectedPermutation(bool allowZeroInResults=false) const
Returns true if the AffineMap represents a subset (i.e.
Definition: AffineMap.cpp:521
AffineMap getMinorSubMap(unsigned numResults) const
Returns the map consisting of the most minor numResults results.
Definition: AffineMap.cpp:578
uint64_t getLargestKnownDivisorOfMapExprs()
Get the largest known divisor of all map expressions.
Definition: AffineMap.cpp:251
constexpr AffineMap()=default
bool isEmpty() const
Returns true if this affine map is an empty map, i.e., () -> ().
Definition: AffineMap.cpp:297
std::optional< unsigned > getResultPosition(AffineExpr input) const
Extracts the first result position where input dimension resides.
Definition: AffineMap.cpp:349
unsigned getNumSymbols() const
Definition: AffineMap.cpp:328
bool isMinorIdentityWithBroadcasting(SmallVectorImpl< unsigned > *broadcastedDims=nullptr) const
Returns true if this affine map is a minor identity up to broadcasted dimensions which are indicated ...
Definition: AffineMap.cpp:124
unsigned getNumDims() const
Definition: AffineMap.cpp:324
ArrayRef< AffineExpr > getResults() const
Definition: AffineMap.cpp:337
SmallVector< int64_t > getConstantResults() const
Returns the constant results of this map.
Definition: AffineMap.cpp:316
bool isPermutationOfMinorIdentityWithBroadcasting(SmallVectorImpl< unsigned > &permutedDims) const
Return true if this affine map can be converted to a minor identity with broadcast by doing a permute...
Definition: AffineMap.cpp:164
LogicalResult constantFold(ArrayRef< Attribute > operandConstants, SmallVectorImpl< Attribute > &results) const
Folds the results of the application of an affine map on the provided operands to a constant if possi...
Definition: AffineMap.cpp:365
bool isSymbolIdentity() const
Returns true if this affine map is an identity affine map on the symbol identifiers.
Definition: AffineMap.cpp:285
unsigned getNumResults() const
Definition: AffineMap.cpp:332
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:426
unsigned getNumInputs() const
Definition: AffineMap.cpp:333
static SmallVector< AffineMap, 4 > inferFromExprList(ArrayRef< ArrayRef< AffineExpr >> exprsList)
Returns a vector of AffineMaps; each with as many results as exprs.size(), as many dims as the larges...
Definition: AffineMap.cpp:242
AffineExpr getResult(unsigned idx) const
Definition: AffineMap.cpp:341
AffineMap replace(AffineExpr expr, AffineExpr replacement, unsigned numResultDims, unsigned numResultSyms) const
Sparse replace method.
Definition: AffineMap.cpp:441
static AffineMap getPermutationMap(ArrayRef< unsigned > permutation, MLIRContext *context)
Returns an AffineMap representing a permutation.
Definition: AffineMap.cpp:212
void walkExprs(llvm::function_ref< void(AffineExpr)> callback) const
Walk all of the AffineExpr's in this mapping.
Definition: AffineMap.cpp:416
static AffineMap getConstantMap(int64_t val, MLIRContext *context)
Returns a single constant result affine map.
Definition: AffineMap.cpp:102
AffineMap getSubMap(ArrayRef< unsigned > resultPos) const
Returns the map consisting of the resultPos subset.
Definition: AffineMap.cpp:557
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
Definition: AffineMap.cpp:482
bool isIdentity() const
Returns true if this affine map is an identity affine map.
Definition: AffineMap.cpp:273
bool isPermutation() const
Returns true if the AffineMap represents a symbol-less permutation map.
Definition: AffineMap.cpp:551
A symbolic identifier appearing in an affine expression.
Definition: AffineExpr.h:224
unsigned getPosition() const
Definition: AffineExpr.cpp:508
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:60
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:262
LLVM_ATTRIBUTE_ALWAYS_INLINE MPInt gcd(const MPInt &a, const MPInt &b)
Definition: MPInt.h:399
This header declares functions that assist transformations in the MemRef dialect.
AffineMap simplifyAffineMap(AffineMap map)
Simplifies an affine map by simplifying its underlying AffineExpr results.
Definition: AffineMap.cpp:648
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
AffineMap expandDimsToRank(AffineMap map, int64_t rank, const llvm::SmallBitVector &projectedDimensions)
Expand map to operate on rank dims while projecting out the dims in projectedDimensions.
Definition: AffineMap.cpp:829
AffineMap removeDuplicateExprs(AffineMap map)
Returns a map with the same dimension and symbol count as map, but whose results are the unique affin...
Definition: AffineMap.cpp:658
llvm::SmallBitVector getUnusedSymbolsBitVector(ArrayRef< AffineMap > maps)
Definition: AffineMap.cpp:816
AffineMap inverseAndBroadcastProjectedPermutation(AffineMap map)
Return the reverse map of a projected permutation where the projected dimensions are transformed into...
Definition: AffineMap.cpp:691
int64_t floorDiv(int64_t lhs, int64_t rhs)
Returns the result of MLIR's floordiv operation on constants.
Definition: MathExtras.h:33
int64_t ceilDiv(int64_t lhs, int64_t rhs)
Returns the result of MLIR's ceildiv operation on constants.
Definition: MathExtras.h:23
AffineMap inversePermutation(AffineMap map)
Returns a map of codomain to domain dimensions such that the first codomain dimension for a particula...
Definition: AffineMap.cpp:667
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:56
AffineMap concatAffineMaps(ArrayRef< AffineMap > maps)
Concatenates a list of maps into a single AffineMap, stepping over potentially empty maps.
Definition: AffineMap.cpp:712
@ CeilDiv
RHS of ceildiv is always a constant or a symbolic expression.
@ Mul
RHS of mul is always a constant or a symbolic expression.
@ Mod
RHS of mod is always a constant or a symbolic expression with a positive value.
@ DimId
Dimensional identifier.
@ FloorDiv
RHS of floordiv is always a constant or a symbolic expression.
@ Constant
Constant integer.
@ SymbolId
Symbolic identifier.
AffineMap compressSymbols(AffineMap map, const llvm::SmallBitVector &unusedSymbols)
Drop the symbols that are listed in unusedSymbols.
Definition: AffineMap.cpp:634
static void getMaxDimAndSymbol(ArrayRef< AffineExprContainer > exprsList, int64_t &maxDim, int64_t &maxSym)
Calculates maximum dimension and symbol positions from the expressions in exprsLists and stores them ...
Definition: AffineMap.h:632
AffineMap compressUnusedDims(AffineMap map)
Drop the dims that are not used.
Definition: AffineMap.cpp:625
AffineMap compressDims(AffineMap map, const llvm::SmallBitVector &unusedDims)
Drop the dims that are listed in unusedDims.
Definition: AffineMap.cpp:620
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
Definition: AffineExpr.cpp:527
AffineMap getProjectedMap(AffineMap map, const llvm::SmallBitVector &projectedDimensions, bool compressDimsFlag=true, bool compressSymbolsFlag=true)
Calls projectDims(map, projectedDimensions, compressDimsFlag).
Definition: AffineMap.cpp:794
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
llvm::SmallBitVector getUnusedDimsBitVector(ArrayRef< AffineMap > maps)
Definition: AffineMap.cpp:804
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:502
AffineMap projectDims(AffineMap map, const llvm::SmallBitVector &projectedDimensions, bool compressDimsFlag=false)
Returns the map that results from projecting out the dimensions specified in projectedDimensions.
Definition: AffineMap.cpp:780
AffineMap compressUnusedSymbols(AffineMap map)
Drop the symbols that are not used.
Definition: AffineMap.cpp:639
AffineMap projectSymbols(AffineMap map, const llvm::SmallBitVector &projectedSymbols, bool compressSymbolsFlag=false)
Symbol counterpart of projectDims.
Definition: AffineMap.cpp:787
AffineExpr getAffineSymbolExpr(unsigned position, MLIRContext *context)
Definition: AffineExpr.cpp:512
int64_t mod(int64_t lhs, int64_t rhs)
Returns MLIR's mod operation on constants.
Definition: MathExtras.h:45
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26
void reset(AffineMap map)
Resets this MutableAffineMap with 'map'.
Definition: AffineMap.cpp:845
AffineMap getAffineMap() const
Get the AffineMap corresponding to this MutableAffineMap.
Definition: AffineMap.cpp:872
AffineExpr getResult(unsigned idx) const
Definition: AffineMap.h:377
bool isMultipleOf(unsigned idx, int64_t factor) const
Returns true if the idx'th result expression is a multiple of factor.
Definition: AffineMap.cpp:853
unsigned getNumResults() const
Definition: AffineMap.h:379
void simplify()
Simplify the (result) expressions in this map using analysis (used by.
Definition: AffineMap.cpp:864
ArrayRef< AffineExpr > results() const
The affine expressions for this (multi-dimensional) map.