1 //===- AffineMap.cpp - MLIR Affine Map Classes ----------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "mlir/IR/AffineMap.h"
10 #include "AffineMapDetail.h"
11 #include "mlir/IR/AffineExpr.h"
12 #include "mlir/IR/Builders.h"
13 #include "mlir/IR/BuiltinAttributes.h"
14 #include "mlir/IR/BuiltinTypes.h"
15 #include "llvm/ADT/STLExtras.h"
16 #include "llvm/ADT/SmallBitVector.h"
17 #include "llvm/ADT/SmallSet.h"
18 #include "llvm/ADT/SmallVector.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/Support/MathExtras.h"
21 #include "llvm/Support/raw_ostream.h"
25 #include <type_traits>
29 using llvm::divideCeilSigned
;
30 using llvm::divideFloorSigned
;
35 // AffineExprConstantFolder evaluates an affine expression using constant
36 // operands passed in 'operandConsts'. Returns an IntegerAttr attribute
37 // representing the constant value of the affine expression evaluated on
38 // constant 'operandConsts', or nullptr if it can't be folded.
39 class AffineExprConstantFolder
{
41 AffineExprConstantFolder(unsigned numDims
, ArrayRef
<Attribute
> operandConsts
)
42 : numDims(numDims
), operandConsts(operandConsts
) {}
44 /// Attempt to constant fold the specified affine expr, or return null on
46 IntegerAttr
constantFold(AffineExpr expr
) {
47 if (auto result
= constantFoldImpl(expr
))
48 return IntegerAttr::get(IndexType::get(expr
.getContext()), *result
);
52 bool hasPoison() const { return hasPoison_
; }
55 std::optional
<int64_t> constantFoldImpl(AffineExpr expr
) {
56 switch (expr
.getKind()) {
57 case AffineExprKind::Add
:
58 return constantFoldBinExpr(
59 expr
, [](int64_t lhs
, int64_t rhs
) { return lhs
+ rhs
; });
60 case AffineExprKind::Mul
:
61 return constantFoldBinExpr(
62 expr
, [](int64_t lhs
, int64_t rhs
) { return lhs
* rhs
; });
63 case AffineExprKind::Mod
:
64 return constantFoldBinExpr(
65 expr
, [this](int64_t lhs
, int64_t rhs
) -> std::optional
<int64_t> {
72 case AffineExprKind::FloorDiv
:
73 return constantFoldBinExpr(
74 expr
, [this](int64_t lhs
, int64_t rhs
) -> std::optional
<int64_t> {
79 return divideFloorSigned(lhs
, rhs
);
81 case AffineExprKind::CeilDiv
:
82 return constantFoldBinExpr(
83 expr
, [this](int64_t lhs
, int64_t rhs
) -> std::optional
<int64_t> {
88 return divideCeilSigned(lhs
, rhs
);
90 case AffineExprKind::Constant
:
91 return cast
<AffineConstantExpr
>(expr
).getValue();
92 case AffineExprKind::DimId
:
93 if (auto attr
= llvm::dyn_cast_or_null
<IntegerAttr
>(
94 operandConsts
[cast
<AffineDimExpr
>(expr
).getPosition()]))
97 case AffineExprKind::SymbolId
:
98 if (auto attr
= llvm::dyn_cast_or_null
<IntegerAttr
>(
99 operandConsts
[numDims
+
100 cast
<AffineSymbolExpr
>(expr
).getPosition()]))
101 return attr
.getInt();
104 llvm_unreachable("Unknown AffineExpr");
107 // TODO: Change these to operate on APInts too.
108 std::optional
<int64_t> constantFoldBinExpr(
110 llvm::function_ref
<std::optional
<int64_t>(int64_t, int64_t)> op
) {
111 auto binOpExpr
= cast
<AffineBinaryOpExpr
>(expr
);
112 if (auto lhs
= constantFoldImpl(binOpExpr
.getLHS()))
113 if (auto rhs
= constantFoldImpl(binOpExpr
.getRHS()))
114 return op(*lhs
, *rhs
);
118 // The number of dimension operands in AffineMap containing this expression.
120 // The constant valued operands used to evaluate this AffineExpr.
121 ArrayRef
<Attribute
> operandConsts
;
122 bool hasPoison_
{false};
127 /// Returns a single constant result affine map.
128 AffineMap
AffineMap::getConstantMap(int64_t val
, MLIRContext
*context
) {
129 return get(/*dimCount=*/0, /*symbolCount=*/0,
130 {getAffineConstantExpr(val
, context
)});
133 /// Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most
134 /// minor dimensions.
135 AffineMap
AffineMap::getMinorIdentityMap(unsigned dims
, unsigned results
,
136 MLIRContext
*context
) {
137 assert(dims
>= results
&& "Dimension mismatch");
138 auto id
= AffineMap::getMultiDimIdentityMap(dims
, context
);
139 return AffineMap::get(dims
, 0, id
.getResults().take_back(results
), context
);
142 AffineMap
AffineMap::getFilteredIdentityMap(
143 MLIRContext
*ctx
, unsigned numDims
,
144 llvm::function_ref
<bool(AffineDimExpr
)> keepDimFilter
) {
145 auto identityMap
= getMultiDimIdentityMap(numDims
, ctx
);
147 // Apply filter to results.
148 llvm::SmallBitVector
dropDimResults(numDims
);
149 for (auto [idx
, resultExpr
] : llvm::enumerate(identityMap
.getResults()))
150 dropDimResults
[idx
] = !keepDimFilter(cast
<AffineDimExpr
>(resultExpr
));
152 return identityMap
.dropResults(dropDimResults
);
155 bool AffineMap::isMinorIdentity() const {
156 return getNumDims() >= getNumResults() &&
158 getMinorIdentityMap(getNumDims(), getNumResults(), getContext());
161 SmallVector
<unsigned> AffineMap::getBroadcastDims() const {
162 SmallVector
<unsigned> broadcastedDims
;
163 for (const auto &[resIdx
, expr
] : llvm::enumerate(getResults())) {
164 if (auto constExpr
= dyn_cast
<AffineConstantExpr
>(expr
)) {
165 if (constExpr
.getValue() != 0)
167 broadcastedDims
.push_back(resIdx
);
171 return broadcastedDims
;
174 /// Returns true if this affine map is a minor identity up to broadcasted
175 /// dimensions which are indicated by value 0 in the result.
176 bool AffineMap::isMinorIdentityWithBroadcasting(
177 SmallVectorImpl
<unsigned> *broadcastedDims
) const {
179 broadcastedDims
->clear();
180 if (getNumDims() < getNumResults())
182 unsigned suffixStart
= getNumDims() - getNumResults();
183 for (const auto &idxAndExpr
: llvm::enumerate(getResults())) {
184 unsigned resIdx
= idxAndExpr
.index();
185 AffineExpr expr
= idxAndExpr
.value();
186 if (auto constExpr
= dyn_cast
<AffineConstantExpr
>(expr
)) {
187 // Each result may be either a constant 0 (broadcasted dimension).
188 if (constExpr
.getValue() != 0)
191 broadcastedDims
->push_back(resIdx
);
192 } else if (auto dimExpr
= dyn_cast
<AffineDimExpr
>(expr
)) {
193 // Or it may be the input dimension corresponding to this result position.
194 if (dimExpr
.getPosition() != suffixStart
+ resIdx
)
203 /// Return true if this affine map can be converted to a minor identity with
204 /// broadcast by doing a permute. Return a permutation (there may be
205 /// several) to apply to get to a minor identity with broadcasts.
207 /// * (d0, d1, d2) -> (0, d1) maps to minor identity (d1, 0 = d2) with
208 /// perm = [1, 0] and broadcast d2
209 /// * (d0, d1, d2) -> (d0, 0) cannot be mapped to a minor identity by
210 /// permutation + broadcast
211 /// * (d0, d1, d2, d3) -> (0, d1, d3) maps to minor identity (d1, 0 = d2, d3)
212 /// with perm = [1, 0, 2] and broadcast d2
213 /// * (d0, d1) -> (d1, 0, 0, d0) maps to minor identity (d0, d1) with extra
214 /// leading broadcat dimensions. The map returned would be (0, 0, d0, d1) with
215 /// perm = [3, 0, 1, 2]
216 bool AffineMap::isPermutationOfMinorIdentityWithBroadcasting(
217 SmallVectorImpl
<unsigned> &permutedDims
) const {
218 unsigned projectionStart
=
219 getNumResults() < getNumInputs() ? getNumInputs() - getNumResults() : 0;
220 permutedDims
.clear();
221 SmallVector
<unsigned> broadcastDims
;
222 permutedDims
.resize(getNumResults(), 0);
223 // If there are more results than input dimensions we want the new map to
224 // start with broadcast dimensions in order to be a minor identity with
226 unsigned leadingBroadcast
=
227 getNumResults() > getNumInputs() ? getNumResults() - getNumInputs() : 0;
228 llvm::SmallBitVector
dimFound(std::max(getNumInputs(), getNumResults()),
230 for (const auto &idxAndExpr
: llvm::enumerate(getResults())) {
231 unsigned resIdx
= idxAndExpr
.index();
232 AffineExpr expr
= idxAndExpr
.value();
233 // Each result may be either a constant 0 (broadcast dimension) or a
235 if (auto constExpr
= dyn_cast
<AffineConstantExpr
>(expr
)) {
236 if (constExpr
.getValue() != 0)
238 broadcastDims
.push_back(resIdx
);
239 } else if (auto dimExpr
= dyn_cast
<AffineDimExpr
>(expr
)) {
240 if (dimExpr
.getPosition() < projectionStart
)
242 unsigned newPosition
=
243 dimExpr
.getPosition() - projectionStart
+ leadingBroadcast
;
244 permutedDims
[resIdx
] = newPosition
;
245 dimFound
[newPosition
] = true;
250 // Find a permuation for the broadcast dimension. Since they are broadcasted
251 // any valid permutation is acceptable. We just permute the dim into a slot
252 // without an existing dimension.
254 for (auto dim
: broadcastDims
) {
255 while (pos
< dimFound
.size() && dimFound
[pos
]) {
258 permutedDims
[dim
] = pos
++;
263 /// Returns an AffineMap representing a permutation.
264 AffineMap
AffineMap::getPermutationMap(ArrayRef
<unsigned> permutation
,
265 MLIRContext
*context
) {
266 assert(!permutation
.empty() &&
267 "Cannot create permutation map from empty permutation vector");
268 const auto *m
= llvm::max_element(permutation
);
269 auto permutationMap
= getMultiDimMapWithTargets(*m
+ 1, permutation
, context
);
270 assert(permutationMap
.isPermutation() && "Invalid permutation vector");
271 return permutationMap
;
273 AffineMap
AffineMap::getPermutationMap(ArrayRef
<int64_t> permutation
,
274 MLIRContext
*context
) {
275 SmallVector
<unsigned> perm
= llvm::map_to_vector(
276 permutation
, [](int64_t i
) { return static_cast<unsigned>(i
); });
277 return AffineMap::getPermutationMap(perm
, context
);
280 AffineMap
AffineMap::getMultiDimMapWithTargets(unsigned numDims
,
281 ArrayRef
<unsigned> targets
,
282 MLIRContext
*context
) {
283 SmallVector
<AffineExpr
, 4> affExprs
;
284 for (unsigned t
: targets
)
285 affExprs
.push_back(getAffineDimExpr(t
, context
));
286 AffineMap result
= AffineMap::get(/*dimCount=*/numDims
, /*symbolCount=*/0,
291 /// Creates an affine map each for each list of AffineExpr's in `exprsList`
292 /// while inferring the right number of dimensional and symbolic inputs needed
293 /// based on the maximum dimensional and symbolic identifier appearing in the
295 template <typename AffineExprContainer
>
296 static SmallVector
<AffineMap
, 4>
297 inferFromExprList(ArrayRef
<AffineExprContainer
> exprsList
,
298 MLIRContext
*context
) {
299 if (exprsList
.empty())
301 int64_t maxDim
= -1, maxSym
= -1;
302 getMaxDimAndSymbol(exprsList
, maxDim
, maxSym
);
303 SmallVector
<AffineMap
, 4> maps
;
304 maps
.reserve(exprsList
.size());
305 for (const auto &exprs
: exprsList
)
306 maps
.push_back(AffineMap::get(/*dimCount=*/maxDim
+ 1,
307 /*symbolCount=*/maxSym
+ 1, exprs
, context
));
311 SmallVector
<AffineMap
, 4>
312 AffineMap::inferFromExprList(ArrayRef
<ArrayRef
<AffineExpr
>> exprsList
,
313 MLIRContext
*context
) {
314 return ::inferFromExprList(exprsList
, context
);
317 SmallVector
<AffineMap
, 4>
318 AffineMap::inferFromExprList(ArrayRef
<SmallVector
<AffineExpr
, 4>> exprsList
,
319 MLIRContext
*context
) {
320 return ::inferFromExprList(exprsList
, context
);
323 uint64_t AffineMap::getLargestKnownDivisorOfMapExprs() {
325 for (AffineExpr resultExpr
: getResults()) {
326 uint64_t thisGcd
= resultExpr
.getLargestKnownDivisor();
327 gcd
= std::gcd(gcd
, thisGcd
);
330 gcd
= std::numeric_limits
<uint64_t>::max();
334 AffineMap
AffineMap::getMultiDimIdentityMap(unsigned numDims
,
335 MLIRContext
*context
) {
336 SmallVector
<AffineExpr
, 4> dimExprs
;
337 dimExprs
.reserve(numDims
);
338 for (unsigned i
= 0; i
< numDims
; ++i
)
339 dimExprs
.push_back(mlir::getAffineDimExpr(i
, context
));
340 return get(/*dimCount=*/numDims
, /*symbolCount=*/0, dimExprs
, context
);
343 MLIRContext
*AffineMap::getContext() const { return map
->context
; }
345 bool AffineMap::isIdentity() const {
346 if (getNumDims() != getNumResults())
348 ArrayRef
<AffineExpr
> results
= getResults();
349 for (unsigned i
= 0, numDims
= getNumDims(); i
< numDims
; ++i
) {
350 auto expr
= dyn_cast
<AffineDimExpr
>(results
[i
]);
351 if (!expr
|| expr
.getPosition() != i
)
357 bool AffineMap::isSymbolIdentity() const {
358 if (getNumSymbols() != getNumResults())
360 ArrayRef
<AffineExpr
> results
= getResults();
361 for (unsigned i
= 0, numSymbols
= getNumSymbols(); i
< numSymbols
; ++i
) {
362 auto expr
= dyn_cast
<AffineDimExpr
>(results
[i
]);
363 if (!expr
|| expr
.getPosition() != i
)
369 bool AffineMap::isEmpty() const {
370 return getNumDims() == 0 && getNumSymbols() == 0 && getNumResults() == 0;
373 bool AffineMap::isSingleConstant() const {
374 return getNumResults() == 1 && isa
<AffineConstantExpr
>(getResult(0));
377 bool AffineMap::isConstant() const {
378 return llvm::all_of(getResults(), llvm::IsaPred
<AffineConstantExpr
>);
381 int64_t AffineMap::getSingleConstantResult() const {
382 assert(isSingleConstant() && "map must have a single constant result");
383 return cast
<AffineConstantExpr
>(getResult(0)).getValue();
386 SmallVector
<int64_t> AffineMap::getConstantResults() const {
387 assert(isConstant() && "map must have only constant results");
388 SmallVector
<int64_t> result
;
389 for (auto expr
: getResults())
390 result
.emplace_back(cast
<AffineConstantExpr
>(expr
).getValue());
394 unsigned AffineMap::getNumDims() const {
395 assert(map
&& "uninitialized map storage");
398 unsigned AffineMap::getNumSymbols() const {
399 assert(map
&& "uninitialized map storage");
400 return map
->numSymbols
;
402 unsigned AffineMap::getNumResults() const { return getResults().size(); }
403 unsigned AffineMap::getNumInputs() const {
404 assert(map
&& "uninitialized map storage");
405 return map
->numDims
+ map
->numSymbols
;
407 ArrayRef
<AffineExpr
> AffineMap::getResults() const {
408 assert(map
&& "uninitialized map storage");
409 return map
->results();
411 AffineExpr
AffineMap::getResult(unsigned idx
) const {
412 return getResults()[idx
];
415 unsigned AffineMap::getDimPosition(unsigned idx
) const {
416 return cast
<AffineDimExpr
>(getResult(idx
)).getPosition();
419 std::optional
<unsigned> AffineMap::getResultPosition(AffineExpr input
) const {
420 if (!isa
<AffineDimExpr
>(input
))
423 for (unsigned i
= 0, numResults
= getNumResults(); i
< numResults
; i
++) {
424 if (getResult(i
) == input
)
431 /// Folds the results of the application of an affine map on the provided
432 /// operands to a constant if possible. Returns false if the folding happens,
434 LogicalResult
AffineMap::constantFold(ArrayRef
<Attribute
> operandConstants
,
435 SmallVectorImpl
<Attribute
> &results
,
436 bool *hasPoison
) const {
437 // Attempt partial folding.
438 SmallVector
<int64_t, 2> integers
;
439 partialConstantFold(operandConstants
, &integers
, hasPoison
);
441 // If all expressions folded to a constant, populate results with attributes
442 // containing those constants.
443 if (integers
.empty())
446 auto range
= llvm::map_range(integers
, [this](int64_t i
) {
447 return IntegerAttr::get(IndexType::get(getContext()), i
);
449 results
.append(range
.begin(), range
.end());
453 AffineMap
AffineMap::partialConstantFold(ArrayRef
<Attribute
> operandConstants
,
454 SmallVectorImpl
<int64_t> *results
,
455 bool *hasPoison
) const {
456 assert(getNumInputs() == operandConstants
.size());
458 // Fold each of the result expressions.
459 AffineExprConstantFolder
exprFolder(getNumDims(), operandConstants
);
460 SmallVector
<AffineExpr
, 4> exprs
;
461 exprs
.reserve(getNumResults());
463 for (auto expr
: getResults()) {
464 auto folded
= exprFolder
.constantFold(expr
);
465 if (exprFolder
.hasPoison() && hasPoison
) {
469 // If did not fold to a constant, keep the original expression, and clear
470 // the integer results vector.
473 getAffineConstantExpr(folded
.getInt(), folded
.getContext()));
475 results
->push_back(folded
.getInt());
477 exprs
.push_back(expr
);
485 return get(getNumDims(), getNumSymbols(), exprs
, getContext());
488 /// Walk all of the AffineExpr's in this mapping. Each node in an expression
489 /// tree is visited in postorder.
490 void AffineMap::walkExprs(llvm::function_ref
<void(AffineExpr
)> callback
) const {
491 for (auto expr
: getResults())
495 /// This method substitutes any uses of dimensions and symbols (e.g.
496 /// dim#0 with dimReplacements[0]) in subexpressions and returns the modified
497 /// expression mapping. Because this can be used to eliminate dims and
498 /// symbols, the client needs to specify the number of dims and symbols in
499 /// the result. The returned map always has the same number of results.
500 AffineMap
AffineMap::replaceDimsAndSymbols(ArrayRef
<AffineExpr
> dimReplacements
,
501 ArrayRef
<AffineExpr
> symReplacements
,
502 unsigned numResultDims
,
503 unsigned numResultSyms
) const {
504 SmallVector
<AffineExpr
, 8> results
;
505 results
.reserve(getNumResults());
506 for (auto expr
: getResults())
508 expr
.replaceDimsAndSymbols(dimReplacements
, symReplacements
));
509 return get(numResultDims
, numResultSyms
, results
, getContext());
512 /// Sparse replace method. Apply AffineExpr::replace(`expr`, `replacement`) to
513 /// each of the results and return a new AffineMap with the new results and
514 /// with the specified number of dims and symbols.
515 AffineMap
AffineMap::replace(AffineExpr expr
, AffineExpr replacement
,
516 unsigned numResultDims
,
517 unsigned numResultSyms
) const {
518 SmallVector
<AffineExpr
, 4> newResults
;
519 newResults
.reserve(getNumResults());
520 for (AffineExpr e
: getResults())
521 newResults
.push_back(e
.replace(expr
, replacement
));
522 return AffineMap::get(numResultDims
, numResultSyms
, newResults
, getContext());
525 /// Sparse replace method. Apply AffineExpr::replace(`map`) to each of the
526 /// results and return a new AffineMap with the new results and with the
527 /// specified number of dims and symbols.
528 AffineMap
AffineMap::replace(const DenseMap
<AffineExpr
, AffineExpr
> &map
,
529 unsigned numResultDims
,
530 unsigned numResultSyms
) const {
531 SmallVector
<AffineExpr
, 4> newResults
;
532 newResults
.reserve(getNumResults());
533 for (AffineExpr e
: getResults())
534 newResults
.push_back(e
.replace(map
));
535 return AffineMap::get(numResultDims
, numResultSyms
, newResults
, getContext());
539 AffineMap::replace(const DenseMap
<AffineExpr
, AffineExpr
> &map
) const {
540 SmallVector
<AffineExpr
, 4> newResults
;
541 newResults
.reserve(getNumResults());
542 for (AffineExpr e
: getResults())
543 newResults
.push_back(e
.replace(map
));
544 return AffineMap::inferFromExprList(newResults
, getContext()).front();
547 AffineMap
AffineMap::dropResults(const llvm::SmallBitVector
&positions
) const {
548 auto exprs
= llvm::to_vector
<4>(getResults());
549 // TODO: this is a pretty terrible API .. is there anything better?
550 for (auto pos
= positions
.find_last(); pos
!= -1;
551 pos
= positions
.find_prev(pos
))
552 exprs
.erase(exprs
.begin() + pos
);
553 return AffineMap::get(getNumDims(), getNumSymbols(), exprs
, getContext());
556 AffineMap
AffineMap::compose(AffineMap map
) const {
557 assert(getNumDims() == map
.getNumResults() && "Number of results mismatch");
558 // Prepare `map` by concatenating the symbols and rewriting its exprs.
559 unsigned numDims
= map
.getNumDims();
560 unsigned numSymbolsThisMap
= getNumSymbols();
561 unsigned numSymbols
= numSymbolsThisMap
+ map
.getNumSymbols();
562 SmallVector
<AffineExpr
, 8> newDims(numDims
);
563 for (unsigned idx
= 0; idx
< numDims
; ++idx
) {
564 newDims
[idx
] = getAffineDimExpr(idx
, getContext());
566 SmallVector
<AffineExpr
, 8> newSymbols(numSymbols
- numSymbolsThisMap
);
567 for (unsigned idx
= numSymbolsThisMap
; idx
< numSymbols
; ++idx
) {
568 newSymbols
[idx
- numSymbolsThisMap
] =
569 getAffineSymbolExpr(idx
, getContext());
572 map
.replaceDimsAndSymbols(newDims
, newSymbols
, numDims
, numSymbols
);
573 SmallVector
<AffineExpr
, 8> exprs
;
574 exprs
.reserve(getResults().size());
575 for (auto expr
: getResults())
576 exprs
.push_back(expr
.compose(newMap
));
577 return AffineMap::get(numDims
, numSymbols
, exprs
, map
.getContext());
580 SmallVector
<int64_t, 4> AffineMap::compose(ArrayRef
<int64_t> values
) const {
581 assert(getNumSymbols() == 0 && "Expected symbol-less map");
582 SmallVector
<AffineExpr
, 4> exprs
;
583 exprs
.reserve(values
.size());
584 MLIRContext
*ctx
= getContext();
585 for (auto v
: values
)
586 exprs
.push_back(getAffineConstantExpr(v
, ctx
));
587 auto resMap
= compose(AffineMap::get(0, 0, exprs
, ctx
));
588 SmallVector
<int64_t, 4> res
;
589 res
.reserve(resMap
.getNumResults());
590 for (auto e
: resMap
.getResults())
591 res
.push_back(cast
<AffineConstantExpr
>(e
).getValue());
595 size_t AffineMap::getNumOfZeroResults() const {
597 for (auto expr
: getResults()) {
598 auto constExpr
= dyn_cast
<AffineConstantExpr
>(expr
);
599 if (constExpr
&& constExpr
.getValue() == 0)
606 AffineMap
AffineMap::dropZeroResults() {
607 auto exprs
= llvm::to_vector(getResults());
608 SmallVector
<AffineExpr
> newExprs
;
610 for (auto expr
: getResults()) {
611 auto constExpr
= dyn_cast
<AffineConstantExpr
>(expr
);
612 if (!constExpr
|| constExpr
.getValue() != 0)
613 newExprs
.push_back(expr
);
615 return AffineMap::get(getNumDims(), getNumSymbols(), newExprs
, getContext());
618 bool AffineMap::isProjectedPermutation(bool allowZeroInResults
) const {
619 if (getNumSymbols() > 0)
622 // Having more results than inputs means that results have duplicated dims or
623 // zeros that can't be mapped to input dims.
624 if (getNumResults() > getNumInputs())
627 SmallVector
<bool, 8> seen(getNumInputs(), false);
628 // A projected permutation can have, at most, only one instance of each input
629 // dimension in the result expressions. Zeros are allowed as long as the
630 // number of result expressions is lower or equal than the number of input
632 for (auto expr
: getResults()) {
633 if (auto dim
= dyn_cast
<AffineDimExpr
>(expr
)) {
634 if (seen
[dim
.getPosition()])
636 seen
[dim
.getPosition()] = true;
638 auto constExpr
= dyn_cast
<AffineConstantExpr
>(expr
);
639 if (!allowZeroInResults
|| !constExpr
|| constExpr
.getValue() != 0)
644 // Results are either dims or zeros and zeros can be mapped to input dims.
648 bool AffineMap::isPermutation() const {
649 if (getNumDims() != getNumResults())
651 return isProjectedPermutation();
654 AffineMap
AffineMap::getSubMap(ArrayRef
<unsigned> resultPos
) const {
655 SmallVector
<AffineExpr
, 4> exprs
;
656 exprs
.reserve(resultPos
.size());
657 for (auto idx
: resultPos
)
658 exprs
.push_back(getResult(idx
));
659 return AffineMap::get(getNumDims(), getNumSymbols(), exprs
, getContext());
662 AffineMap
AffineMap::getSliceMap(unsigned start
, unsigned length
) const {
663 return AffineMap::get(getNumDims(), getNumSymbols(),
664 getResults().slice(start
, length
), getContext());
667 AffineMap
AffineMap::getMajorSubMap(unsigned numResults
) const {
670 if (numResults
> getNumResults())
672 return getSliceMap(0, numResults
);
675 AffineMap
AffineMap::getMinorSubMap(unsigned numResults
) const {
678 if (numResults
> getNumResults())
680 return getSliceMap(getNumResults() - numResults
, numResults
);
683 /// Implementation detail to compress multiple affine maps with a compressionFun
684 /// that is expected to be either compressUnusedDims or compressUnusedSymbols.
685 /// The implementation keeps track of num dims and symbols across the different
687 static SmallVector
<AffineMap
> compressUnusedListImpl(
688 ArrayRef
<AffineMap
> maps
,
689 llvm::function_ref
<AffineMap(AffineMap
)> compressionFun
) {
691 return SmallVector
<AffineMap
>();
692 SmallVector
<AffineExpr
> allExprs
;
693 allExprs
.reserve(maps
.size() * maps
.front().getNumResults());
694 unsigned numDims
= maps
.front().getNumDims(),
695 numSymbols
= maps
.front().getNumSymbols();
696 for (auto m
: maps
) {
697 assert(numDims
== m
.getNumDims() && numSymbols
== m
.getNumSymbols() &&
698 "expected maps with same num dims and symbols");
699 llvm::append_range(allExprs
, m
.getResults());
701 AffineMap unifiedMap
= compressionFun(
702 AffineMap::get(numDims
, numSymbols
, allExprs
, maps
.front().getContext()));
703 unsigned unifiedNumDims
= unifiedMap
.getNumDims(),
704 unifiedNumSymbols
= unifiedMap
.getNumSymbols();
705 ArrayRef
<AffineExpr
> unifiedResults
= unifiedMap
.getResults();
706 SmallVector
<AffineMap
> res
;
707 res
.reserve(maps
.size());
708 for (auto m
: maps
) {
709 res
.push_back(AffineMap::get(unifiedNumDims
, unifiedNumSymbols
,
710 unifiedResults
.take_front(m
.getNumResults()),
712 unifiedResults
= unifiedResults
.drop_front(m
.getNumResults());
717 AffineMap
mlir::compressDims(AffineMap map
,
718 const llvm::SmallBitVector
&unusedDims
) {
719 return projectDims(map
, unusedDims
, /*compressDimsFlag=*/true);
722 AffineMap
mlir::compressUnusedDims(AffineMap map
) {
723 return compressDims(map
, getUnusedDimsBitVector({map
}));
726 SmallVector
<AffineMap
> mlir::compressUnusedDims(ArrayRef
<AffineMap
> maps
) {
727 return compressUnusedListImpl(
728 maps
, [](AffineMap m
) { return compressUnusedDims(m
); });
731 AffineMap
mlir::compressSymbols(AffineMap map
,
732 const llvm::SmallBitVector
&unusedSymbols
) {
733 return projectSymbols(map
, unusedSymbols
, /*compressSymbolsFlag=*/true);
736 AffineMap
mlir::compressUnusedSymbols(AffineMap map
) {
737 return compressSymbols(map
, getUnusedSymbolsBitVector({map
}));
740 SmallVector
<AffineMap
> mlir::compressUnusedSymbols(ArrayRef
<AffineMap
> maps
) {
741 return compressUnusedListImpl(
742 maps
, [](AffineMap m
) { return compressUnusedSymbols(m
); });
745 AffineMap
mlir::foldAttributesIntoMap(Builder
&b
, AffineMap map
,
746 ArrayRef
<OpFoldResult
> operands
,
747 SmallVector
<Value
> &remainingValues
) {
748 SmallVector
<AffineExpr
> dimReplacements
, symReplacements
;
750 for (int64_t i
= 0; i
< map
.getNumDims(); ++i
) {
751 if (auto attr
= operands
[i
].dyn_cast
<Attribute
>()) {
752 dimReplacements
.push_back(
753 b
.getAffineConstantExpr(cast
<IntegerAttr
>(attr
).getInt()));
755 dimReplacements
.push_back(b
.getAffineDimExpr(numDims
++));
756 remainingValues
.push_back(operands
[i
].get
<Value
>());
759 int64_t numSymbols
= 0;
760 for (int64_t i
= 0; i
< map
.getNumSymbols(); ++i
) {
761 if (auto attr
= operands
[i
+ map
.getNumDims()].dyn_cast
<Attribute
>()) {
762 symReplacements
.push_back(
763 b
.getAffineConstantExpr(cast
<IntegerAttr
>(attr
).getInt()));
765 symReplacements
.push_back(b
.getAffineSymbolExpr(numSymbols
++));
766 remainingValues
.push_back(operands
[i
+ map
.getNumDims()].get
<Value
>());
769 return map
.replaceDimsAndSymbols(dimReplacements
, symReplacements
, numDims
,
773 AffineMap
mlir::simplifyAffineMap(AffineMap map
) {
774 SmallVector
<AffineExpr
, 8> exprs
;
775 for (auto e
: map
.getResults()) {
777 simplifyAffineExpr(e
, map
.getNumDims(), map
.getNumSymbols()));
779 return AffineMap::get(map
.getNumDims(), map
.getNumSymbols(), exprs
,
783 AffineMap
mlir::removeDuplicateExprs(AffineMap map
) {
784 auto results
= map
.getResults();
785 SmallVector
<AffineExpr
, 4> uniqueExprs(results
);
786 uniqueExprs
.erase(llvm::unique(uniqueExprs
), uniqueExprs
.end());
787 return AffineMap::get(map
.getNumDims(), map
.getNumSymbols(), uniqueExprs
,
791 AffineMap
mlir::inversePermutation(AffineMap map
) {
794 assert(map
.getNumSymbols() == 0 && "expected map without symbols");
795 SmallVector
<AffineExpr
, 4> exprs(map
.getNumDims());
796 for (const auto &en
: llvm::enumerate(map
.getResults())) {
797 auto expr
= en
.value();
798 // Skip non-permutations.
799 if (auto d
= dyn_cast
<AffineDimExpr
>(expr
)) {
800 if (exprs
[d
.getPosition()])
802 exprs
[d
.getPosition()] = getAffineDimExpr(en
.index(), d
.getContext());
805 SmallVector
<AffineExpr
, 4> seenExprs
;
806 seenExprs
.reserve(map
.getNumDims());
807 for (auto expr
: exprs
)
809 seenExprs
.push_back(expr
);
810 if (seenExprs
.size() != map
.getNumInputs())
812 return AffineMap::get(map
.getNumResults(), 0, seenExprs
, map
.getContext());
815 AffineMap
mlir::inverseAndBroadcastProjectedPermutation(AffineMap map
) {
816 assert(map
.isProjectedPermutation(/*allowZeroInResults=*/true));
817 MLIRContext
*context
= map
.getContext();
818 AffineExpr zero
= mlir::getAffineConstantExpr(0, context
);
819 // Start with all the results as 0.
820 SmallVector
<AffineExpr
, 4> exprs(map
.getNumInputs(), zero
);
821 for (unsigned i
: llvm::seq(unsigned(0), map
.getNumResults())) {
822 // Skip zeros from input map. 'exprs' is already initialized to zero.
823 if (auto constExpr
= dyn_cast
<AffineConstantExpr
>(map
.getResult(i
))) {
824 assert(constExpr
.getValue() == 0 &&
825 "Unexpected constant in projected permutation");
830 // Reverse each dimension existing in the original map result.
831 exprs
[map
.getDimPosition(i
)] = getAffineDimExpr(i
, context
);
833 return AffineMap::get(map
.getNumResults(), /*symbolCount=*/0, exprs
, context
);
836 AffineMap
mlir::concatAffineMaps(ArrayRef
<AffineMap
> maps
) {
837 unsigned numResults
= 0, numDims
= 0, numSymbols
= 0;
839 numResults
+= m
.getNumResults();
840 SmallVector
<AffineExpr
, 8> results
;
841 results
.reserve(numResults
);
842 for (auto m
: maps
) {
843 for (auto res
: m
.getResults())
844 results
.push_back(res
.shiftSymbols(m
.getNumSymbols(), numSymbols
));
846 numSymbols
+= m
.getNumSymbols();
847 numDims
= std::max(m
.getNumDims(), numDims
);
849 return AffineMap::get(numDims
, numSymbols
, results
,
850 maps
.front().getContext());
853 /// Common implementation to project out dimensions or symbols from an affine
854 /// map based on the template type.
855 /// Additionally, if 'compress' is true, the projected out dimensions or symbols
856 /// are also dropped from the resulting map.
857 template <typename AffineDimOrSymExpr
>
858 static AffineMap
projectCommonImpl(AffineMap map
,
859 const llvm::SmallBitVector
&toProject
,
861 static_assert(llvm::is_one_of
<AffineDimOrSymExpr
, AffineDimExpr
,
862 AffineSymbolExpr
>::value
,
863 "expected AffineDimExpr or AffineSymbolExpr");
865 constexpr bool isDim
= std::is_same
<AffineDimOrSymExpr
, AffineDimExpr
>::value
;
866 int64_t numDimOrSym
= (isDim
) ? map
.getNumDims() : map
.getNumSymbols();
867 SmallVector
<AffineExpr
> replacements
;
868 replacements
.reserve(numDimOrSym
);
870 auto createNewDimOrSym
= (isDim
) ? getAffineDimExpr
: getAffineSymbolExpr
;
872 using replace_fn_ty
=
873 std::function
<AffineExpr(AffineExpr
, ArrayRef
<AffineExpr
>)>;
874 replace_fn_ty replaceDims
= [](AffineExpr e
,
875 ArrayRef
<AffineExpr
> replacements
) {
876 return e
.replaceDims(replacements
);
878 replace_fn_ty replaceSymbols
= [](AffineExpr e
,
879 ArrayRef
<AffineExpr
> replacements
) {
880 return e
.replaceSymbols(replacements
);
882 replace_fn_ty replaceNewDimOrSym
= (isDim
) ? replaceDims
: replaceSymbols
;
884 MLIRContext
*context
= map
.getContext();
885 int64_t newNumDimOrSym
= 0;
886 for (unsigned dimOrSym
= 0; dimOrSym
< numDimOrSym
; ++dimOrSym
) {
887 if (toProject
.test(dimOrSym
)) {
888 replacements
.push_back(getAffineConstantExpr(0, context
));
891 int64_t newPos
= compress
? newNumDimOrSym
++ : dimOrSym
;
892 replacements
.push_back(createNewDimOrSym(newPos
, context
));
894 SmallVector
<AffineExpr
> resultExprs
;
895 resultExprs
.reserve(map
.getNumResults());
896 for (auto e
: map
.getResults())
897 resultExprs
.push_back(replaceNewDimOrSym(e
, replacements
));
899 int64_t numDims
= (compress
&& isDim
) ? newNumDimOrSym
: map
.getNumDims();
900 int64_t numSyms
= (compress
&& !isDim
) ? newNumDimOrSym
: map
.getNumSymbols();
901 return AffineMap::get(numDims
, numSyms
, resultExprs
, context
);
904 AffineMap
mlir::projectDims(AffineMap map
,
905 const llvm::SmallBitVector
&projectedDimensions
,
906 bool compressDimsFlag
) {
907 return projectCommonImpl
<AffineDimExpr
>(map
, projectedDimensions
,
911 AffineMap
mlir::projectSymbols(AffineMap map
,
912 const llvm::SmallBitVector
&projectedSymbols
,
913 bool compressSymbolsFlag
) {
914 return projectCommonImpl
<AffineSymbolExpr
>(map
, projectedSymbols
,
915 compressSymbolsFlag
);
918 AffineMap
mlir::getProjectedMap(AffineMap map
,
919 const llvm::SmallBitVector
&projectedDimensions
,
920 bool compressDimsFlag
,
921 bool compressSymbolsFlag
) {
922 map
= projectDims(map
, projectedDimensions
, compressDimsFlag
);
923 if (compressSymbolsFlag
)
924 map
= compressUnusedSymbols(map
);
928 llvm::SmallBitVector
mlir::getUnusedDimsBitVector(ArrayRef
<AffineMap
> maps
) {
929 unsigned numDims
= maps
[0].getNumDims();
930 llvm::SmallBitVector
numDimsBitVector(numDims
, true);
931 for (AffineMap m
: maps
) {
932 for (unsigned i
= 0; i
< numDims
; ++i
) {
933 if (m
.isFunctionOfDim(i
))
934 numDimsBitVector
.reset(i
);
937 return numDimsBitVector
;
940 llvm::SmallBitVector
mlir::getUnusedSymbolsBitVector(ArrayRef
<AffineMap
> maps
) {
941 unsigned numSymbols
= maps
[0].getNumSymbols();
942 llvm::SmallBitVector
numSymbolsBitVector(numSymbols
, true);
943 for (AffineMap m
: maps
) {
944 for (unsigned i
= 0; i
< numSymbols
; ++i
) {
945 if (m
.isFunctionOfSymbol(i
))
946 numSymbolsBitVector
.reset(i
);
949 return numSymbolsBitVector
;
953 mlir::expandDimsToRank(AffineMap map
, int64_t rank
,
954 const llvm::SmallBitVector
&projectedDimensions
) {
955 auto id
= AffineMap::getMultiDimIdentityMap(rank
, map
.getContext());
956 AffineMap proj
= id
.dropResults(projectedDimensions
);
957 return map
.compose(proj
);
960 //===----------------------------------------------------------------------===//
962 //===----------------------------------------------------------------------===//
964 MutableAffineMap::MutableAffineMap(AffineMap map
)
965 : results(map
.getResults()), numDims(map
.getNumDims()),
966 numSymbols(map
.getNumSymbols()), context(map
.getContext()) {}
968 void MutableAffineMap::reset(AffineMap map
) {
970 numDims
= map
.getNumDims();
971 numSymbols
= map
.getNumSymbols();
972 context
= map
.getContext();
973 llvm::append_range(results
, map
.getResults());
976 bool MutableAffineMap::isMultipleOf(unsigned idx
, int64_t factor
) const {
977 return results
[idx
].isMultipleOf(factor
);
980 // Simplifies the result affine expressions of this map. The expressions
981 // have to be pure for the simplification implemented.
982 void MutableAffineMap::simplify() {
983 // Simplify each of the results if possible.
984 // TODO: functional-style map
985 for (unsigned i
= 0, e
= getNumResults(); i
< e
; i
++) {
986 results
[i
] = simplifyAffineExpr(getResult(i
), numDims
, numSymbols
);
990 AffineMap
MutableAffineMap::getAffineMap() const {
991 return AffineMap::get(numDims
, numSymbols
, results
, context
);