MLIR 23.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"
12#include "mlir/IR/Builders.h"
15#include "llvm/ADT/STLExtras.h"
16#include "llvm/ADT/SmallBitVector.h"
17#include "llvm/ADT/SmallVector.h"
18#include "llvm/Support/MathExtras.h"
19#include <numeric>
20#include <optional>
21#include <type_traits>
22
23using namespace mlir;
24
25using llvm::divideCeilSigned;
26using llvm::divideFloorSigned;
27using llvm::mod;
28
29namespace {
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.
35class AffineExprConstantFolder {
36public:
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 bool hasPoison() const { return hasPoison_; }
49
50private:
51 std::optional<int64_t> constantFoldImpl(AffineExpr expr) {
52 switch (expr.getKind()) {
53 case AffineExprKind::Add:
54 return constantFoldBinExpr(
55 expr, [](int64_t lhs, int64_t rhs) { return lhs + rhs; });
56 case AffineExprKind::Mul:
57 return constantFoldBinExpr(
58 expr, [](int64_t lhs, int64_t rhs) { return lhs * rhs; });
59 case AffineExprKind::Mod:
60 return constantFoldBinExpr(
61 expr, [this](int64_t lhs, int64_t rhs) -> std::optional<int64_t> {
62 if (rhs < 1) {
63 hasPoison_ = true;
64 return std::nullopt;
65 }
66 return mod(lhs, rhs);
67 });
68 case AffineExprKind::FloorDiv:
69 return constantFoldBinExpr(
70 expr, [this](int64_t lhs, int64_t rhs) -> std::optional<int64_t> {
71 if (rhs == 0) {
72 hasPoison_ = true;
73 return std::nullopt;
74 }
75 return divideFloorSigned(lhs, rhs);
76 });
77 case AffineExprKind::CeilDiv:
78 return constantFoldBinExpr(
79 expr, [this](int64_t lhs, int64_t rhs) -> std::optional<int64_t> {
80 if (rhs == 0) {
81 hasPoison_ = true;
82 return std::nullopt;
83 }
84 return divideCeilSigned(lhs, rhs);
85 });
86 case AffineExprKind::Constant:
87 return cast<AffineConstantExpr>(expr).getValue();
88 case AffineExprKind::DimId:
89 if (auto attr = llvm::dyn_cast_or_null<IntegerAttr>(
90 operandConsts[cast<AffineDimExpr>(expr).getPosition()]))
91 return attr.getInt();
92 return std::nullopt;
93 case AffineExprKind::SymbolId:
94 if (auto attr = llvm::dyn_cast_or_null<IntegerAttr>(
95 operandConsts[numDims +
96 cast<AffineSymbolExpr>(expr).getPosition()]))
97 return attr.getInt();
98 return std::nullopt;
99 }
100 llvm_unreachable("Unknown AffineExpr");
101 }
102
103 // TODO: Change these to operate on APInts too.
104 std::optional<int64_t> constantFoldBinExpr(
105 AffineExpr expr,
106 llvm::function_ref<std::optional<int64_t>(int64_t, int64_t)> op) {
107 auto binOpExpr = cast<AffineBinaryOpExpr>(expr);
108 if (auto lhs = constantFoldImpl(binOpExpr.getLHS()))
109 if (auto rhs = constantFoldImpl(binOpExpr.getRHS()))
110 return op(*lhs, *rhs);
111 return std::nullopt;
112 }
113
114 // The number of dimension operands in AffineMap containing this expression.
115 unsigned numDims;
116 // The constant valued operands used to evaluate this AffineExpr.
117 ArrayRef<Attribute> operandConsts;
118 bool hasPoison_{false};
119};
120
121} // namespace
122
123/// Returns a single constant result affine map.
125 return get(/*dimCount=*/0, /*symbolCount=*/0,
126 {getAffineConstantExpr(val, context)});
127}
128
129/// Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most
130/// minor dimensions.
131AffineMap AffineMap::getMinorIdentityMap(unsigned dims, unsigned results,
132 MLIRContext *context) {
133 assert(dims >= results && "Dimension mismatch");
134 auto id = AffineMap::getMultiDimIdentityMap(dims, context);
135 return AffineMap::get(dims, 0, id.getResults().take_back(results), context);
136}
137
139 MLIRContext *ctx, unsigned numDims,
140 llvm::function_ref<bool(AffineDimExpr)> keepDimFilter) {
141 auto identityMap = getMultiDimIdentityMap(numDims, ctx);
142
143 // Apply filter to results.
144 llvm::SmallBitVector dropDimResults(numDims);
145 for (auto [idx, resultExpr] : llvm::enumerate(identityMap.getResults()))
146 dropDimResults[idx] = !keepDimFilter(cast<AffineDimExpr>(resultExpr));
147
148 return identityMap.dropResults(dropDimResults);
149}
150
152 return getNumDims() >= getNumResults() &&
153 *this ==
155}
156
158 SmallVector<unsigned> broadcastedDims;
159 for (const auto &[resIdx, expr] : llvm::enumerate(getResults())) {
160 if (auto constExpr = dyn_cast<AffineConstantExpr>(expr)) {
161 if (constExpr.getValue() != 0)
162 continue;
163 broadcastedDims.push_back(resIdx);
164 }
165 }
166
167 return broadcastedDims;
168}
169
170/// Returns true if this affine map is a minor identity up to broadcasted
171/// dimensions which are indicated by value 0 in the result.
173 SmallVectorImpl<unsigned> *broadcastedDims) const {
174 if (broadcastedDims)
175 broadcastedDims->clear();
176 if (getNumDims() < getNumResults())
177 return false;
178 unsigned suffixStart = getNumDims() - getNumResults();
179 for (const auto &idxAndExpr : llvm::enumerate(getResults())) {
180 unsigned resIdx = idxAndExpr.index();
181 AffineExpr expr = idxAndExpr.value();
182 if (auto constExpr = dyn_cast<AffineConstantExpr>(expr)) {
183 // Each result may be either a constant 0 (broadcasted dimension).
184 if (constExpr.getValue() != 0)
185 return false;
186 if (broadcastedDims)
187 broadcastedDims->push_back(resIdx);
188 } else if (auto dimExpr = dyn_cast<AffineDimExpr>(expr)) {
189 // Or it may be the input dimension corresponding to this result position.
190 if (dimExpr.getPosition() != suffixStart + resIdx)
191 return false;
192 } else {
193 return false;
194 }
195 }
196 return true;
197}
198
199/// Return true if this affine map can be converted to a minor identity with
200/// broadcast by doing a permute. Return a permutation (there may be
201/// several) to apply to get to a minor identity with broadcasts.
202/// Ex:
203/// * (d0, d1, d2) -> (0, d1) maps to minor identity (d1, 0 = d2) with
204/// perm = [1, 0] and broadcast d2
205/// * (d0, d1, d2) -> (d0, 0) cannot be mapped to a minor identity by
206/// permutation + broadcast
207/// * (d0, d1, d2, d3) -> (0, d1, d3) maps to minor identity (d1, 0 = d2, d3)
208/// with perm = [1, 0, 2] and broadcast d2
209/// * (d0, d1) -> (d1, 0, 0, d0) maps to minor identity (d0, d1) with extra
210/// leading broadcat dimensions. The map returned would be (0, 0, d0, d1) with
211/// perm = [3, 0, 1, 2]
213 SmallVectorImpl<unsigned> &permutedDims) const {
214 unsigned projectionStart =
216 permutedDims.clear();
217 SmallVector<unsigned> broadcastDims;
218 permutedDims.resize(getNumResults(), 0);
219 // If there are more results than input dimensions we want the new map to
220 // start with broadcast dimensions in order to be a minor identity with
221 // broadcasting.
222 unsigned leadingBroadcast =
224 llvm::SmallBitVector dimFound(std::max(getNumInputs(), getNumResults()),
225 false);
226 for (const auto &idxAndExpr : llvm::enumerate(getResults())) {
227 unsigned resIdx = idxAndExpr.index();
228 AffineExpr expr = idxAndExpr.value();
229 // Each result may be either a constant 0 (broadcast dimension) or a
230 // dimension.
231 if (auto constExpr = dyn_cast<AffineConstantExpr>(expr)) {
232 if (constExpr.getValue() != 0)
233 return false;
234 broadcastDims.push_back(resIdx);
235 } else if (auto dimExpr = dyn_cast<AffineDimExpr>(expr)) {
236 if (dimExpr.getPosition() < projectionStart)
237 return false;
238 unsigned newPosition =
239 dimExpr.getPosition() - projectionStart + leadingBroadcast;
240 permutedDims[resIdx] = newPosition;
241 dimFound[newPosition] = true;
242 } else {
243 return false;
244 }
245 }
246 // Find a permuation for the broadcast dimension. Since they are broadcasted
247 // any valid permutation is acceptable. We just permute the dim into a slot
248 // without an existing dimension.
249 unsigned pos = 0;
250 for (auto dim : broadcastDims) {
251 while (pos < dimFound.size() && dimFound[pos]) {
252 pos++;
253 }
254 permutedDims[dim] = pos++;
255 }
256 return true;
257}
258
259/// Returns an AffineMap representing a permutation.
261 MLIRContext *context) {
262 assert(!permutation.empty() &&
263 "Cannot create permutation map from empty permutation vector");
264 const auto *m = llvm::max_element(permutation);
265 auto permutationMap = getMultiDimMapWithTargets(*m + 1, permutation, context);
266 assert(permutationMap.isPermutation() && "Invalid permutation vector");
267 return permutationMap;
268}
270 MLIRContext *context) {
271 SmallVector<unsigned> perm = llvm::map_to_vector(
272 permutation, [](int64_t i) { return static_cast<unsigned>(i); });
273 return AffineMap::getPermutationMap(perm, context);
274}
275
277 ArrayRef<unsigned> targets,
278 MLIRContext *context) {
279 // Inline size chosen empirically based on compilation profiling.
280 // Profiled: 3.1M calls, avg=4.1+-3.7. N=8 covers ~86% of cases inline.
282 for (unsigned t : targets)
283 affExprs.push_back(getAffineDimExpr(t, context));
284 AffineMap result = AffineMap::get(/*dimCount=*/numDims, /*symbolCount=*/0,
285 affExprs, context);
286 return result;
287}
288
289/// Creates an affine map each for each list of AffineExpr's in `exprsList`
290/// while inferring the right number of dimensional and symbolic inputs needed
291/// based on the maximum dimensional and symbolic identifier appearing in the
292/// expressions.
293template <typename AffineExprContainer>
296 MLIRContext *context) {
297 if (exprsList.empty())
298 return {};
299 int64_t maxDim = -1, maxSym = -1;
300 getMaxDimAndSymbol(exprsList, maxDim, maxSym);
302 maps.reserve(exprsList.size());
303 for (const auto &exprs : exprsList)
304 maps.push_back(AffineMap::get(/*dimCount=*/maxDim + 1,
305 /*symbolCount=*/maxSym + 1, exprs, context));
306 return maps;
307}
308
311 MLIRContext *context) {
312 return ::inferFromExprList(exprsList, context);
313}
314
317 MLIRContext *context) {
318 return ::inferFromExprList(exprsList, context);
319}
320
322 uint64_t gcd = 0;
323 for (AffineExpr resultExpr : getResults()) {
324 uint64_t thisGcd = resultExpr.getLargestKnownDivisor();
325 gcd = std::gcd(gcd, thisGcd);
326 }
327 if (gcd == 0)
328 gcd = std::numeric_limits<uint64_t>::max();
329 return gcd;
330}
331
333 MLIRContext *context) {
335 dimExprs.reserve(numDims);
336 for (unsigned i = 0; i < numDims; ++i)
337 dimExprs.push_back(mlir::getAffineDimExpr(i, context));
338 return get(/*dimCount=*/numDims, /*symbolCount=*/0, dimExprs, context);
339}
340
341MLIRContext *AffineMap::getContext() const { return map->context; }
342
344 if (getNumDims() != getNumResults())
345 return false;
347 for (unsigned i = 0, numDims = getNumDims(); i < numDims; ++i) {
348 auto expr = dyn_cast<AffineDimExpr>(results[i]);
349 if (!expr || expr.getPosition() != i)
350 return false;
351 }
352 return true;
353}
354
356 if (getNumSymbols() != getNumResults())
357 return false;
359 for (unsigned i = 0, numSymbols = getNumSymbols(); i < numSymbols; ++i) {
360 auto expr = dyn_cast<AffineDimExpr>(results[i]);
361 if (!expr || expr.getPosition() != i)
362 return false;
363 }
364 return true;
365}
366
367bool AffineMap::isEmpty() const {
368 return getNumDims() == 0 && getNumSymbols() == 0 && getNumResults() == 0;
369}
370
372 return getNumResults() == 1 && isa<AffineConstantExpr>(getResult(0));
373}
374
376 return llvm::all_of(getResults(), llvm::IsaPred<AffineConstantExpr>);
377}
378
380 assert(isSingleConstant() && "map must have a single constant result");
381 return cast<AffineConstantExpr>(getResult(0)).getValue();
382}
383
385 assert(isConstant() && "map must have only constant results");
387 for (auto expr : getResults())
388 result.emplace_back(cast<AffineConstantExpr>(expr).getValue());
389 return result;
390}
391
392unsigned AffineMap::getNumDims() const {
393 assert(map && "uninitialized map storage");
394 return map->numDims;
395}
396unsigned AffineMap::getNumSymbols() const {
397 assert(map && "uninitialized map storage");
398 return map->numSymbols;
399}
400unsigned AffineMap::getNumResults() const { return getResults().size(); }
401unsigned AffineMap::getNumInputs() const {
402 assert(map && "uninitialized map storage");
403 return map->numDims + map->numSymbols;
404}
406 assert(map && "uninitialized map storage");
407 return map->results();
408}
409AffineExpr AffineMap::getResult(unsigned idx) const {
410 return getResults()[idx];
411}
412
413unsigned AffineMap::getDimPosition(unsigned idx) const {
414 return cast<AffineDimExpr>(getResult(idx)).getPosition();
415}
416
417std::optional<unsigned> AffineMap::getResultPosition(AffineExpr input) const {
418 if (!isa<AffineDimExpr>(input))
419 return std::nullopt;
420
421 for (unsigned i = 0, numResults = getNumResults(); i < numResults; i++) {
422 if (getResult(i) == input)
423 return i;
424 }
425
426 return std::nullopt;
427}
428
429/// Folds the results of the application of an affine map on the provided
430/// operands to a constant if possible. Returns false if the folding happens,
431/// true otherwise.
432LogicalResult AffineMap::constantFold(ArrayRef<Attribute> operandConstants,
434 bool *hasPoison) const {
435 // Attempt partial folding.
437 partialConstantFold(operandConstants, &integers, hasPoison);
438
439 // If all expressions folded to a constant, populate results with attributes
440 // containing those constants.
441 if (integers.empty())
442 return failure();
443
444 auto range = llvm::map_range(integers, [this](int64_t i) {
445 return IntegerAttr::get(IndexType::get(getContext()), i);
446 });
447 results.append(range.begin(), range.end());
448 return success();
449}
450
453 bool *hasPoison) const {
454 assert(getNumInputs() == operandConstants.size());
455
456 // Fold each of the result expressions.
457 AffineExprConstantFolder exprFolder(getNumDims(), operandConstants);
459 exprs.reserve(getNumResults());
460
461 for (auto expr : getResults()) {
462 auto folded = exprFolder.constantFold(expr);
463 if (exprFolder.hasPoison() && hasPoison) {
464 *hasPoison = true;
465 return {};
466 }
467 // If did not fold to a constant, keep the original expression, and clear
468 // the integer results vector.
469 if (folded) {
470 exprs.push_back(
471 getAffineConstantExpr(folded.getInt(), folded.getContext()));
472 if (results)
473 results->push_back(folded.getInt());
474 } else {
475 exprs.push_back(expr);
476 if (results) {
477 results->clear();
478 results = nullptr;
479 }
480 }
481 }
482
483 return get(getNumDims(), getNumSymbols(), exprs, getContext());
484}
485
486/// Walk all of the AffineExpr's in this mapping. Each node in an expression
487/// tree is visited in postorder.
489 for (auto expr : getResults())
490 expr.walk(callback);
491}
492
493/// This method substitutes any uses of dimensions and symbols (e.g.
494/// dim#0 with dimReplacements[0]) in subexpressions and returns the modified
495/// expression mapping. Because this can be used to eliminate dims and
496/// symbols, the client needs to specify the number of dims and symbols in
497/// the result. The returned map always has the same number of results.
499 ArrayRef<AffineExpr> symReplacements,
500 unsigned numResultDims,
501 unsigned numResultSyms) const {
503 results.reserve(getNumResults());
504 for (auto expr : getResults())
505 results.push_back(
506 expr.replaceDimsAndSymbols(dimReplacements, symReplacements));
507 return get(numResultDims, numResultSyms, results, getContext());
508}
509
510/// Sparse replace method. Apply AffineExpr::replace(`expr`, `replacement`) to
511/// each of the results and return a new AffineMap with the new results and
512/// with the specified number of dims and symbols.
514 unsigned numResultDims,
515 unsigned numResultSyms) const {
517 newResults.reserve(getNumResults());
518 for (AffineExpr e : getResults())
519 newResults.push_back(e.replace(expr, replacement));
520 return AffineMap::get(numResultDims, numResultSyms, newResults, getContext());
521}
522
523/// Sparse replace method. Apply AffineExpr::replace(`map`) to each of the
524/// results and return a new AffineMap with the new results and with the
525/// specified number of dims and symbols.
527 unsigned numResultDims,
528 unsigned numResultSyms) const {
530 newResults.reserve(getNumResults());
531 for (AffineExpr e : getResults())
532 newResults.push_back(e.replace(map));
533 return AffineMap::get(numResultDims, numResultSyms, newResults, getContext());
534}
535
539 newResults.reserve(getNumResults());
540 for (AffineExpr e : getResults())
541 newResults.push_back(e.replace(map));
542 return AffineMap::inferFromExprList(newResults, getContext()).front();
543}
544
545AffineMap AffineMap::dropResults(const llvm::SmallBitVector &positions) const {
546 auto exprs = llvm::to_vector<4>(getResults());
547 // TODO: this is a pretty terrible API .. is there anything better?
548 for (auto pos = positions.find_last(); pos != -1;
549 pos = positions.find_prev(pos))
550 exprs.erase(exprs.begin() + pos);
551 return AffineMap::get(getNumDims(), getNumSymbols(), exprs, getContext());
552}
553
555 assert(getNumDims() == map.getNumResults() && "Number of results mismatch");
556 // Prepare `map` by concatenating the symbols and rewriting its exprs.
557 unsigned numDims = map.getNumDims();
558 unsigned numSymbolsThisMap = getNumSymbols();
559 unsigned numSymbols = numSymbolsThisMap + map.getNumSymbols();
560 SmallVector<AffineExpr, 8> newDims(numDims);
561 for (unsigned idx = 0; idx < numDims; ++idx) {
562 newDims[idx] = getAffineDimExpr(idx, getContext());
563 }
564 SmallVector<AffineExpr, 8> newSymbols(numSymbols - numSymbolsThisMap);
565 for (unsigned idx = numSymbolsThisMap; idx < numSymbols; ++idx) {
566 newSymbols[idx - numSymbolsThisMap] =
568 }
569 auto newMap =
570 map.replaceDimsAndSymbols(newDims, newSymbols, numDims, numSymbols);
572 exprs.reserve(getResults().size());
573 for (auto expr : getResults())
574 exprs.push_back(expr.compose(newMap));
575 return AffineMap::get(numDims, numSymbols, exprs, map.getContext());
576}
577
578// Inline size chosen empirically based on compilation profiling.
579// Profiled: 43.5M calls, avg=3.1+-2.3. N=8 covers ~98% of cases inline.
581 assert(getNumSymbols() == 0 && "Expected symbol-less map");
583 MLIRContext *ctx = getContext();
584 for (int64_t value : values)
585 exprs.push_back(getAffineConstantExpr(value, ctx));
587 res.reserve(getNumResults());
588 for (auto e : getResults())
589 res.push_back(cast<AffineConstantExpr>(e.replaceDims(exprs)).getValue());
590 return res;
591}
592
594 size_t res = 0;
595 for (auto expr : getResults()) {
596 auto constExpr = dyn_cast<AffineConstantExpr>(expr);
597 if (constExpr && constExpr.getValue() == 0)
598 res++;
599 }
600
601 return res;
602}
603
606
607 for (auto expr : getResults()) {
608 auto constExpr = dyn_cast<AffineConstantExpr>(expr);
609 if (!constExpr || constExpr.getValue() != 0)
610 newExprs.push_back(expr);
611 }
612 return AffineMap::get(getNumDims(), getNumSymbols(), newExprs, getContext());
613}
614
615bool AffineMap::isProjectedPermutation(bool allowZeroInResults) const {
616 if (getNumSymbols() > 0)
617 return false;
618
619 // Having more results than inputs means that results have duplicated dims or
620 // zeros that can't be mapped to input dims.
621 if (getNumResults() > getNumInputs())
622 return false;
623
624 SmallVector<bool, 8> seen(getNumInputs(), false);
625 // A projected permutation can have, at most, only one instance of each input
626 // dimension in the result expressions. Zeros are allowed as long as the
627 // number of result expressions is lower or equal than the number of input
628 // expressions.
629 for (auto expr : getResults()) {
630 if (auto dim = dyn_cast<AffineDimExpr>(expr)) {
631 if (seen[dim.getPosition()])
632 return false;
633 seen[dim.getPosition()] = true;
634 } else {
635 auto constExpr = dyn_cast<AffineConstantExpr>(expr);
636 if (!allowZeroInResults || !constExpr || constExpr.getValue() != 0)
637 return false;
638 }
639 }
640
641 // Results are either dims or zeros and zeros can be mapped to input dims.
642 return true;
643}
644
646 if (getNumDims() != getNumResults())
647 return false;
648 return isProjectedPermutation();
649}
650
653 exprs.reserve(resultPos.size());
654 for (auto idx : resultPos)
655 exprs.push_back(getResult(idx));
656 return AffineMap::get(getNumDims(), getNumSymbols(), exprs, getContext());
657}
658
659AffineMap AffineMap::getSliceMap(unsigned start, unsigned length) const {
661 getResults().slice(start, length), getContext());
662}
663
664AffineMap AffineMap::getMajorSubMap(unsigned numResults) const {
665 if (numResults == 0)
666 return AffineMap();
667 if (numResults > getNumResults())
668 return *this;
669 return getSliceMap(0, numResults);
670}
671
672AffineMap AffineMap::getMinorSubMap(unsigned numResults) const {
673 if (numResults == 0)
674 return AffineMap();
675 if (numResults > getNumResults())
676 return *this;
677 return getSliceMap(getNumResults() - numResults, numResults);
678}
679
680/// Implementation detail to compress multiple affine maps with a compressionFun
681/// that is expected to be either compressUnusedDims or compressUnusedSymbols.
682/// The implementation keeps track of num dims and symbols across the different
683/// affine maps.
686 llvm::function_ref<AffineMap(AffineMap)> compressionFun) {
687 if (maps.empty())
688 return SmallVector<AffineMap>();
690 allExprs.reserve(maps.size() * maps.front().getNumResults());
691 unsigned numDims = maps.front().getNumDims(),
692 numSymbols = maps.front().getNumSymbols();
693 for (auto m : maps) {
694 assert(numDims == m.getNumDims() && numSymbols == m.getNumSymbols() &&
695 "expected maps with same num dims and symbols");
696 llvm::append_range(allExprs, m.getResults());
697 }
698 AffineMap unifiedMap = compressionFun(
699 AffineMap::get(numDims, numSymbols, allExprs, maps.front().getContext()));
700 unsigned unifiedNumDims = unifiedMap.getNumDims(),
701 unifiedNumSymbols = unifiedMap.getNumSymbols();
702 ArrayRef<AffineExpr> unifiedResults = unifiedMap.getResults();
704 res.reserve(maps.size());
705 for (auto m : maps) {
706 res.push_back(AffineMap::get(unifiedNumDims, unifiedNumSymbols,
707 unifiedResults.take_front(m.getNumResults()),
708 m.getContext()));
709 unifiedResults = unifiedResults.drop_front(m.getNumResults());
710 }
711 return res;
712}
713
715 const llvm::SmallBitVector &unusedDims) {
716 return projectDims(map, unusedDims, /*compressDimsFlag=*/true);
717}
718
722
727
729 const llvm::SmallBitVector &unusedSymbols) {
730 return projectSymbols(map, unusedSymbols, /*compressSymbolsFlag=*/true);
731}
732
736
741
743 ArrayRef<OpFoldResult> operands,
744 SmallVector<Value> &remainingValues) {
745 SmallVector<AffineExpr> dimReplacements, symReplacements;
746 int64_t numDims = 0;
747 for (int64_t i = 0; i < map.getNumDims(); ++i) {
748 if (auto attr = dyn_cast<Attribute>(operands[i])) {
749 dimReplacements.push_back(
750 b.getAffineConstantExpr(cast<IntegerAttr>(attr).getInt()));
751 } else {
752 dimReplacements.push_back(b.getAffineDimExpr(numDims++));
753 remainingValues.push_back(cast<Value>(operands[i]));
754 }
755 }
756 int64_t numSymbols = 0;
757 for (int64_t i = 0; i < map.getNumSymbols(); ++i) {
758 if (auto attr = dyn_cast<Attribute>(operands[i + map.getNumDims()])) {
759 symReplacements.push_back(
760 b.getAffineConstantExpr(cast<IntegerAttr>(attr).getInt()));
761 } else {
762 symReplacements.push_back(b.getAffineSymbolExpr(numSymbols++));
763 remainingValues.push_back(cast<Value>(operands[i + map.getNumDims()]));
764 }
765 }
766 return map.replaceDimsAndSymbols(dimReplacements, symReplacements, numDims,
767 numSymbols);
768}
769
772 for (auto e : map.getResults()) {
773 exprs.push_back(
775 }
776 return AffineMap::get(map.getNumDims(), map.getNumSymbols(), exprs,
777 map.getContext());
778}
779
781 auto results = map.getResults();
782 SmallVector<AffineExpr, 4> uniqueExprs(results);
783 uniqueExprs.erase(llvm::unique(uniqueExprs), uniqueExprs.end());
784 return AffineMap::get(map.getNumDims(), map.getNumSymbols(), uniqueExprs,
785 map.getContext());
786}
787
789 if (map.isEmpty())
790 return map;
791 assert(map.getNumSymbols() == 0 && "expected map without symbols");
793 for (const auto &en : llvm::enumerate(map.getResults())) {
794 auto expr = en.value();
795 // Skip non-permutations.
796 if (auto d = dyn_cast<AffineDimExpr>(expr)) {
797 if (exprs[d.getPosition()])
798 continue;
799 exprs[d.getPosition()] = getAffineDimExpr(en.index(), d.getContext());
800 }
801 }
803 seenExprs.reserve(map.getNumDims());
804 for (auto expr : exprs)
805 if (expr)
806 seenExprs.push_back(expr);
807 if (seenExprs.size() != map.getNumInputs())
808 return AffineMap();
809 return AffineMap::get(map.getNumResults(), 0, seenExprs, map.getContext());
810}
811
813 assert(map.isProjectedPermutation(/*allowZeroInResults=*/true));
814 MLIRContext *context = map.getContext();
815 AffineExpr zero = mlir::getAffineConstantExpr(0, context);
816 // Start with all the results as 0.
817 SmallVector<AffineExpr, 4> exprs(map.getNumInputs(), zero);
818 for (unsigned i : llvm::seq(unsigned(0), map.getNumResults())) {
819 // Skip zeros from input map. 'exprs' is already initialized to zero.
820 if (auto constExpr = dyn_cast<AffineConstantExpr>(map.getResult(i))) {
821 assert(constExpr.getValue() == 0 &&
822 "Unexpected constant in projected permutation");
823 (void)constExpr;
824 continue;
825 }
826
827 // Reverse each dimension existing in the original map result.
828 exprs[map.getDimPosition(i)] = getAffineDimExpr(i, context);
829 }
830 return AffineMap::get(map.getNumResults(), /*symbolCount=*/0, exprs, context);
831}
832
834 MLIRContext *context) {
835 if (maps.empty())
836 return AffineMap::get(context);
837 unsigned numResults = 0, numDims = 0, numSymbols = 0;
838 for (auto m : maps)
839 numResults += m.getNumResults();
841 results.reserve(numResults);
842 for (auto m : maps) {
843 for (auto res : m.getResults())
844 results.push_back(res.shiftSymbols(m.getNumSymbols(), numSymbols));
845
846 numSymbols += m.getNumSymbols();
847 numDims = std::max(m.getNumDims(), numDims);
848 }
849 return AffineMap::get(numDims, numSymbols, results, context);
850}
851
852/// Common implementation to project out dimensions or symbols from an affine
853/// map based on the template type.
854/// Additionally, if 'compress' is true, the projected out dimensions or symbols
855/// are also dropped from the resulting map.
856template <typename AffineDimOrSymExpr>
858 const llvm::SmallBitVector &toProject,
859 bool compress) {
860 static_assert(llvm::is_one_of<AffineDimOrSymExpr, AffineDimExpr,
861 AffineSymbolExpr>::value,
862 "expected AffineDimExpr or AffineSymbolExpr");
863
864 constexpr bool isDim = std::is_same<AffineDimOrSymExpr, AffineDimExpr>::value;
865 int64_t numDimOrSym = (isDim) ? map.getNumDims() : map.getNumSymbols();
866 SmallVector<AffineExpr> replacements;
867 replacements.reserve(numDimOrSym);
868
869 auto createNewDimOrSym = (isDim) ? getAffineDimExpr : getAffineSymbolExpr;
870
871 using replace_fn_ty =
873 replace_fn_ty replaceDims = [](AffineExpr e,
874 ArrayRef<AffineExpr> replacements) {
875 return e.replaceDims(replacements);
876 };
877 replace_fn_ty replaceSymbols = [](AffineExpr e,
878 ArrayRef<AffineExpr> replacements) {
879 return e.replaceSymbols(replacements);
880 };
881 replace_fn_ty replaceNewDimOrSym = (isDim) ? replaceDims : replaceSymbols;
882
883 MLIRContext *context = map.getContext();
884 int64_t newNumDimOrSym = 0;
885 for (unsigned dimOrSym = 0; dimOrSym < numDimOrSym; ++dimOrSym) {
886 if (toProject.test(dimOrSym)) {
887 replacements.push_back(getAffineConstantExpr(0, context));
888 continue;
889 }
890 int64_t newPos = compress ? newNumDimOrSym++ : dimOrSym;
891 replacements.push_back(createNewDimOrSym(newPos, context));
892 }
893 SmallVector<AffineExpr> resultExprs;
894 resultExprs.reserve(map.getNumResults());
895 for (auto e : map.getResults())
896 resultExprs.push_back(replaceNewDimOrSym(e, replacements));
897
898 int64_t numDims = (compress && isDim) ? newNumDimOrSym : map.getNumDims();
899 int64_t numSyms = (compress && !isDim) ? newNumDimOrSym : map.getNumSymbols();
900 return AffineMap::get(numDims, numSyms, resultExprs, context);
901}
902
904 const llvm::SmallBitVector &projectedDimensions,
905 bool compressDimsFlag) {
906 return projectCommonImpl<AffineDimExpr>(map, projectedDimensions,
907 compressDimsFlag);
908}
909
911 const llvm::SmallBitVector &projectedSymbols,
912 bool compressSymbolsFlag) {
913 return projectCommonImpl<AffineSymbolExpr>(map, projectedSymbols,
914 compressSymbolsFlag);
915}
916
918 const llvm::SmallBitVector &projectedDimensions,
919 bool compressDimsFlag,
920 bool compressSymbolsFlag) {
921 map = projectDims(map, projectedDimensions, compressDimsFlag);
922 if (compressSymbolsFlag)
923 map = compressUnusedSymbols(map);
924 return map;
925}
926
928 unsigned numDims = maps[0].getNumDims();
929 llvm::SmallBitVector numDimsBitVector(numDims, true);
930 for (AffineMap m : maps) {
931 for (unsigned i = 0; i < numDims; ++i) {
932 if (m.isFunctionOfDim(i))
933 numDimsBitVector.reset(i);
934 }
935 }
936 return numDimsBitVector;
937}
938
940 unsigned numSymbols = maps[0].getNumSymbols();
941 llvm::SmallBitVector numSymbolsBitVector(numSymbols, true);
942 for (AffineMap m : maps) {
943 for (unsigned i = 0; i < numSymbols; ++i) {
944 if (m.isFunctionOfSymbol(i))
945 numSymbolsBitVector.reset(i);
946 }
947 }
948 return numSymbolsBitVector;
949}
950
953 const llvm::SmallBitVector &projectedDimensions) {
954 auto id = AffineMap::getMultiDimIdentityMap(rank, map.getContext());
955 AffineMap proj = id.dropResults(projectedDimensions);
956 return map.compose(proj);
957}
958
959//===----------------------------------------------------------------------===//
960// MutableAffineMap.
961//===----------------------------------------------------------------------===//
962
964 : results(map.getResults()), numDims(map.getNumDims()),
965 numSymbols(map.getNumSymbols()), context(map.getContext()) {}
966
968 results.clear();
969 numDims = map.getNumDims();
970 numSymbols = map.getNumSymbols();
971 context = map.getContext();
972 llvm::append_range(results, map.getResults());
973}
974
975bool MutableAffineMap::isMultipleOf(unsigned idx, int64_t factor) const {
976 return results[idx].isMultipleOf(factor);
977}
978
979// Simplifies the result affine expressions of this map. The expressions
980// have to be pure for the simplification implemented.
982 // Simplify each of the results if possible.
983 // TODO: functional-style map
984 for (unsigned i = 0, e = getNumResults(); i < e; i++) {
985 results[i] = simplifyAffineExpr(getResult(i), numDims, numSymbols);
986 }
987}
988
990 return AffineMap::get(numDims, numSymbols, results, context);
991}
return success()
lhs
static SmallVector< AffineMap, 4 > inferFromExprList(ArrayRef< AffineExprContainer > exprsList, MLIRContext *context)
Creates an affine map each for each list of AffineExpr's in exprsList while inferring the right numbe...
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...
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...
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
A dimensional identifier appearing in an affine expression.
Definition AffineExpr.h:223
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.
RetT walk(FnT &&callback) const
Walk all of the AffineExpr's in this expression in postorder.
Definition AffineExpr.h:117
AffineExprKind getKind() const
Return the classification for this type.
AffineExpr compose(AffineMap map) const
Compose with an AffineMap.
AffineExpr replaceDims(ArrayRef< AffineExpr > dimReplacements) const
Dim-only version of replaceDimsAndSymbols.
MLIRContext * getContext() const
AffineExpr replaceSymbols(ArrayRef< AffineExpr > symReplacements) const
Symbol-only version of replaceDimsAndSymbols.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
Definition AffineMap.h:46
int64_t getSingleConstantResult() const
Returns the constant result of this map.
static AffineMap getMinorIdentityMap(unsigned dims, unsigned results, MLIRContext *context)
Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most minor dimensions.
AffineMap dropResults(ArrayRef< int64_t > positions) const
Definition AffineMap.h:299
AffineMap getSliceMap(unsigned start, unsigned length) const
Returns the map consisting of length expressions starting from start.
AffineMap getMajorSubMap(unsigned numResults) const
Returns the map consisting of the most major numResults results.
MLIRContext * getContext() const
bool isMinorIdentity() const
Returns true if this affine map is a minor identity, i.e.
unsigned getDimPosition(unsigned idx) const
Extracts the position of the dimensional expression at the given result, when the caller knows it is ...
bool isConstant() const
Returns true if this affine map has only constant results.
static AffineMap getMultiDimIdentityMap(unsigned numDims, MLIRContext *context)
Returns an AffineMap with 'numDims' identity result dim exprs.
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.
bool isProjectedPermutation(bool allowZeroInResults=false) const
Returns true if the AffineMap represents a subset (i.e.
AffineMap getMinorSubMap(unsigned numResults) const
Returns the map consisting of the most minor numResults results.
uint64_t getLargestKnownDivisorOfMapExprs()
Get the largest known divisor of all map expressions.
constexpr AffineMap()=default
bool isEmpty() const
Returns true if this affine map is an empty map, i.e., () -> ().
std::optional< unsigned > getResultPosition(AffineExpr input) const
Extracts the first result position where input dimension resides.
unsigned getNumSymbols() const
bool isMinorIdentityWithBroadcasting(SmallVectorImpl< unsigned > *broadcastedDims=nullptr) const
Returns true if this affine map is a minor identity up to broadcasted dimensions which are indicated ...
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
SmallVector< int64_t > getConstantResults() const
Returns the constant results of this map.
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...
size_t getNumOfZeroResults() const
Returns the number of "zero" results (constant values == 0) in this map.
bool isSymbolIdentity() const
Returns true if this affine map is an identity affine map on the symbol identifiers.
unsigned getNumResults() const
static SmallVector< AffineMap, 4 > inferFromExprList(ArrayRef< ArrayRef< AffineExpr > > exprsList, MLIRContext *context)
Returns a vector of AffineMaps; each with as many results as exprs.size(), as many dims as the larges...
AffineMap replaceDimsAndSymbols(ArrayRef< AffineExpr > dimReplacements, ArrayRef< AffineExpr > symReplacements, unsigned numResultDims, unsigned numResultSyms) const
This method substitutes any uses of dimensions and symbols (e.g.
unsigned getNumInputs() const
AffineExpr getResult(unsigned idx) const
static AffineMap getFilteredIdentityMap(MLIRContext *ctx, unsigned numDims, llvm::function_ref< bool(AffineDimExpr)> keepDimFilter)
Returns an identity affine map with numDims input dimensions and filtered results using keepDimFilter...
AffineMap replace(AffineExpr expr, AffineExpr replacement, unsigned numResultDims, unsigned numResultSyms) const
Sparse replace method.
AffineMap dropZeroResults()
Returns the AffineMap resulting from removing "zero" results (constant values == 0) from this map.
static AffineMap getPermutationMap(ArrayRef< unsigned > permutation, MLIRContext *context)
Returns an AffineMap representing a permutation.
SmallVector< unsigned > getBroadcastDims() const
Returns the list of broadcast dimensions (i.e.
void walkExprs(llvm::function_ref< void(AffineExpr)> callback) const
Walk all of the AffineExpr's in this mapping.
AffineMap partialConstantFold(ArrayRef< Attribute > operandConstants, SmallVectorImpl< int64_t > *results=nullptr, bool *hasPoison=nullptr) const
Propagates the constant operands into this affine map.
static AffineMap getConstantMap(int64_t val, MLIRContext *context)
Returns a single constant result affine map.
static AffineMap getMultiDimMapWithTargets(unsigned numDims, ArrayRef< unsigned > targets, MLIRContext *context)
Returns an affine map with numDims input dimensions and results specified by targets.
AffineMap getSubMap(ArrayRef< unsigned > resultPos) const
Returns the map consisting of the resultPos subset.
LogicalResult constantFold(ArrayRef< Attribute > operandConstants, SmallVectorImpl< Attribute > &results, bool *hasPoison=nullptr) const
Folds the results of the application of an affine map on the provided operands to a constant if possi...
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
bool isIdentity() const
Returns true if this affine map is an identity affine map.
bool isPermutation() const
Returns true if the AffineMap represents a symbol-less permutation map.
A symbolic identifier appearing in an affine expression.
Definition AffineExpr.h:231
This class is a general helper class for creating context-global objects like types,...
Definition Builders.h:51
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
Include the generated interface declarations.
AffineMap simplifyAffineMap(AffineMap map)
Simplifies an affine map by simplifying its underlying AffineExpr results.
AffineMap concatAffineMaps(ArrayRef< AffineMap > maps, MLIRContext *context)
Concatenates a list of maps into a single AffineMap, stepping over potentially empty maps.
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.
AffineMap removeDuplicateExprs(AffineMap map)
Returns a map with the same dimension and symbol count as map, but whose results are the unique affin...
llvm::SmallBitVector getUnusedSymbolsBitVector(ArrayRef< AffineMap > maps)
AffineMap inverseAndBroadcastProjectedPermutation(AffineMap map)
Return the reverse map of a projected permutation where the projected dimensions are transformed into...
AffineMap inversePermutation(AffineMap map)
Returns a map of codomain to domain dimensions such that the first codomain dimension for a particula...
AffineMap compressSymbols(AffineMap map, const llvm::SmallBitVector &unusedSymbols)
Drop the symbols that are listed in unusedSymbols.
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:697
AffineMap compressUnusedDims(AffineMap map)
Drop the dims that are not used.
AffineMap compressDims(AffineMap map, const llvm::SmallBitVector &unusedDims)
Drop the dims that are listed in unusedDims.
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
AffineMap getProjectedMap(AffineMap map, const llvm::SmallBitVector &projectedDimensions, bool compressDimsFlag=true, bool compressSymbolsFlag=true)
Calls projectDims(map, projectedDimensions, compressDimsFlag).
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:120
llvm::SmallBitVector getUnusedDimsBitVector(ArrayRef< AffineMap > maps)
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.
AffineMap projectDims(AffineMap map, const llvm::SmallBitVector &projectedDimensions, bool compressDimsFlag=false)
Returns the map that results from projecting out the dimensions specified in projectedDimensions.
AffineMap compressUnusedSymbols(AffineMap map)
Drop the symbols that are not used.
AffineMap projectSymbols(AffineMap map, const llvm::SmallBitVector &projectedSymbols, bool compressSymbolsFlag=false)
Symbol counterpart of projectDims.
AffineMap foldAttributesIntoMap(Builder &b, AffineMap map, ArrayRef< OpFoldResult > operands, SmallVector< Value > &remainingValues)
Fold all attributes among the given operands into the affine map.
AffineExpr getAffineSymbolExpr(unsigned position, MLIRContext *context)
void reset(AffineMap map)
Resets this MutableAffineMap with 'map'.
MLIRContext * getContext() const
Definition AffineMap.h:443
AffineMap getAffineMap() const
Get the AffineMap corresponding to this MutableAffineMap.
AffineExpr getResult(unsigned idx) const
Definition AffineMap.h:436
unsigned getNumSymbols() const
Definition AffineMap.h:441
bool isMultipleOf(unsigned idx, int64_t factor) const
Returns true if the idx'th result expression is a multiple of factor.
ArrayRef< AffineExpr > getResults() const
Definition AffineMap.h:435
unsigned getNumResults() const
Definition AffineMap.h:438
unsigned getNumDims() const
Definition AffineMap.h:439
void simplify()
Simplify the (result) expressions in this map using analysis (used by.