MLIR 24.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::divideSignedWouldOverflow;
28using llvm::mod;
29
30namespace {
31
32// AffineExprConstantFolder evaluates an affine expression using constant
33// operands passed in 'operandConsts'. Returns an IntegerAttr attribute
34// representing the constant value of the affine expression evaluated on
35// constant 'operandConsts', or nullptr if it can't be folded.
36class AffineExprConstantFolder {
37public:
38 AffineExprConstantFolder(unsigned numDims, ArrayRef<Attribute> operandConsts)
39 : numDims(numDims), operandConsts(operandConsts) {}
40
41 /// Attempt to constant fold the specified affine expr, or return null on
42 /// failure.
43 IntegerAttr constantFold(AffineExpr expr) {
44 if (auto result = constantFoldImpl(expr))
45 return IntegerAttr::get(IndexType::get(expr.getContext()), *result);
46 return nullptr;
47 }
48
49 bool hasPoison() const { return hasPoison_; }
50
51private:
52 std::optional<int64_t> constantFoldImpl(AffineExpr expr) {
53 switch (expr.getKind()) {
54 case AffineExprKind::Add:
55 return constantFoldBinExpr(
56 expr, [](int64_t lhs, int64_t rhs) -> std::optional<int64_t> {
57 int64_t sum;
58 if (llvm::AddOverflow(lhs, rhs, sum))
59 return std::nullopt;
60 return sum;
61 });
62 case AffineExprKind::Mul:
63 return constantFoldBinExpr(
64 expr, [](int64_t lhs, int64_t rhs) -> std::optional<int64_t> {
65 int64_t product;
66 if (llvm::MulOverflow(lhs, rhs, product))
67 return std::nullopt;
68 return product;
69 });
70 case AffineExprKind::Mod:
71 return constantFoldBinExpr(
72 expr, [this](int64_t lhs, int64_t rhs) -> std::optional<int64_t> {
73 if (rhs < 1) {
74 hasPoison_ = true;
75 return std::nullopt;
76 }
77 return mod(lhs, rhs);
78 });
79 case AffineExprKind::FloorDiv:
80 return constantFoldBinExpr(
81 expr, [this](int64_t lhs, int64_t rhs) -> std::optional<int64_t> {
82 if (rhs == 0) {
83 hasPoison_ = true;
84 return std::nullopt;
85 }
86 if (divideSignedWouldOverflow(lhs, rhs))
87 return std::nullopt;
88 return divideFloorSigned(lhs, rhs);
89 });
90 case AffineExprKind::CeilDiv:
91 return constantFoldBinExpr(
92 expr, [this](int64_t lhs, int64_t rhs) -> std::optional<int64_t> {
93 if (rhs == 0) {
94 hasPoison_ = true;
95 return std::nullopt;
96 }
97 if (divideSignedWouldOverflow(lhs, rhs))
98 return std::nullopt;
99 return divideCeilSigned(lhs, rhs);
100 });
101 case AffineExprKind::Constant:
102 return cast<AffineConstantExpr>(expr).getValue();
103 case AffineExprKind::DimId:
104 if (auto attr = llvm::dyn_cast_or_null<IntegerAttr>(
105 operandConsts[cast<AffineDimExpr>(expr).getPosition()]))
106 return attr.getInt();
107 return std::nullopt;
108 case AffineExprKind::SymbolId:
109 if (auto attr = llvm::dyn_cast_or_null<IntegerAttr>(
110 operandConsts[numDims +
111 cast<AffineSymbolExpr>(expr).getPosition()]))
112 return attr.getInt();
113 return std::nullopt;
114 }
115 llvm_unreachable("Unknown AffineExpr");
116 }
117
118 // TODO: Change these to operate on APInts too.
119 std::optional<int64_t> constantFoldBinExpr(
120 AffineExpr expr,
121 llvm::function_ref<std::optional<int64_t>(int64_t, int64_t)> op) {
122 auto binOpExpr = cast<AffineBinaryOpExpr>(expr);
123 if (auto lhs = constantFoldImpl(binOpExpr.getLHS()))
124 if (auto rhs = constantFoldImpl(binOpExpr.getRHS()))
125 return op(*lhs, *rhs);
126 return std::nullopt;
127 }
128
129 // The number of dimension operands in AffineMap containing this expression.
130 unsigned numDims;
131 // The constant valued operands used to evaluate this AffineExpr.
132 ArrayRef<Attribute> operandConsts;
133 bool hasPoison_{false};
134};
135
136} // namespace
137
138/// Returns a single constant result affine map.
140 return get(/*dimCount=*/0, /*symbolCount=*/0,
141 {getAffineConstantExpr(val, context)});
142}
143
144/// Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most
145/// minor dimensions.
146AffineMap AffineMap::getMinorIdentityMap(unsigned dims, unsigned results,
147 MLIRContext *context) {
148 assert(dims >= results && "Dimension mismatch");
149 auto id = AffineMap::getMultiDimIdentityMap(dims, context);
150 return AffineMap::get(dims, 0, id.getResults().take_back(results), context);
151}
152
154 MLIRContext *ctx, unsigned numDims,
155 llvm::function_ref<bool(AffineDimExpr)> keepDimFilter) {
156 auto identityMap = getMultiDimIdentityMap(numDims, ctx);
157
158 // Apply filter to results.
159 llvm::SmallBitVector dropDimResults(numDims);
160 for (auto [idx, resultExpr] : llvm::enumerate(identityMap.getResults()))
161 dropDimResults[idx] = !keepDimFilter(cast<AffineDimExpr>(resultExpr));
162
163 return identityMap.dropResults(dropDimResults);
164}
165
167 return getNumDims() >= getNumResults() &&
168 *this ==
170}
171
173 SmallVector<unsigned> broadcastedDims;
174 for (const auto &[resIdx, expr] : llvm::enumerate(getResults())) {
175 if (auto constExpr = dyn_cast<AffineConstantExpr>(expr)) {
176 if (constExpr.getValue() != 0)
177 continue;
178 broadcastedDims.push_back(resIdx);
179 }
180 }
181
182 return broadcastedDims;
183}
184
185/// Returns true if this affine map is a minor identity up to broadcasted
186/// dimensions which are indicated by value 0 in the result.
188 SmallVectorImpl<unsigned> *broadcastedDims) const {
189 if (broadcastedDims)
190 broadcastedDims->clear();
191 if (getNumDims() < getNumResults())
192 return false;
193 unsigned suffixStart = getNumDims() - getNumResults();
194 for (const auto &idxAndExpr : llvm::enumerate(getResults())) {
195 unsigned resIdx = idxAndExpr.index();
196 AffineExpr expr = idxAndExpr.value();
197 if (auto constExpr = dyn_cast<AffineConstantExpr>(expr)) {
198 // Each result may be either a constant 0 (broadcasted dimension).
199 if (constExpr.getValue() != 0)
200 return false;
201 if (broadcastedDims)
202 broadcastedDims->push_back(resIdx);
203 } else if (auto dimExpr = dyn_cast<AffineDimExpr>(expr)) {
204 // Or it may be the input dimension corresponding to this result position.
205 if (dimExpr.getPosition() != suffixStart + resIdx)
206 return false;
207 } else {
208 return false;
209 }
210 }
211 return true;
212}
213
214/// Return true if this affine map can be converted to a minor identity with
215/// broadcast by doing a permute. Return a permutation (there may be
216/// several) to apply to get to a minor identity with broadcasts.
217/// Ex:
218/// * (d0, d1, d2) -> (0, d1) maps to minor identity (d1, 0 = d2) with
219/// perm = [1, 0] and broadcast d2
220/// * (d0, d1, d2) -> (d0, 0) cannot be mapped to a minor identity by
221/// permutation + broadcast
222/// * (d0, d1, d2, d3) -> (0, d1, d3) maps to minor identity (d1, 0 = d2, d3)
223/// with perm = [1, 0, 2] and broadcast d2
224/// * (d0, d1) -> (d1, 0, 0, d0) maps to minor identity (d0, d1) with extra
225/// leading broadcat dimensions. The map returned would be (0, 0, d0, d1) with
226/// perm = [3, 0, 1, 2]
228 SmallVectorImpl<unsigned> &permutedDims) const {
229 unsigned projectionStart =
231 permutedDims.clear();
232 SmallVector<unsigned> broadcastDims;
233 permutedDims.resize(getNumResults(), 0);
234 // If there are more results than input dimensions we want the new map to
235 // start with broadcast dimensions in order to be a minor identity with
236 // broadcasting.
237 unsigned leadingBroadcast =
239 llvm::SmallBitVector dimFound(std::max(getNumInputs(), getNumResults()),
240 false);
241 for (const auto &idxAndExpr : llvm::enumerate(getResults())) {
242 unsigned resIdx = idxAndExpr.index();
243 AffineExpr expr = idxAndExpr.value();
244 // Each result may be either a constant 0 (broadcast dimension) or a
245 // dimension.
246 if (auto constExpr = dyn_cast<AffineConstantExpr>(expr)) {
247 if (constExpr.getValue() != 0)
248 return false;
249 broadcastDims.push_back(resIdx);
250 } else if (auto dimExpr = dyn_cast<AffineDimExpr>(expr)) {
251 if (dimExpr.getPosition() < projectionStart)
252 return false;
253 unsigned newPosition =
254 dimExpr.getPosition() - projectionStart + leadingBroadcast;
255 permutedDims[resIdx] = newPosition;
256 dimFound[newPosition] = true;
257 } else {
258 return false;
259 }
260 }
261 // Find a permuation for the broadcast dimension. Since they are broadcasted
262 // any valid permutation is acceptable. We just permute the dim into a slot
263 // without an existing dimension.
264 unsigned pos = 0;
265 for (auto dim : broadcastDims) {
266 while (pos < dimFound.size() && dimFound[pos]) {
267 pos++;
268 }
269 permutedDims[dim] = pos++;
270 }
271 return true;
272}
273
274/// Returns an AffineMap representing a permutation.
276 MLIRContext *context) {
277 assert(!permutation.empty() &&
278 "Cannot create permutation map from empty permutation vector");
279 const auto *m = llvm::max_element(permutation);
280 auto permutationMap = getMultiDimMapWithTargets(*m + 1, permutation, context);
281 assert(permutationMap.isPermutation() && "Invalid permutation vector");
282 return permutationMap;
283}
285 MLIRContext *context) {
286 SmallVector<unsigned> perm = llvm::map_to_vector(
287 permutation, [](int64_t i) { return static_cast<unsigned>(i); });
288 return AffineMap::getPermutationMap(perm, context);
289}
290
292 ArrayRef<unsigned> targets,
293 MLIRContext *context) {
294 // Inline size chosen empirically based on compilation profiling.
295 // Profiled: 3.1M calls, avg=4.1+-3.7. N=8 covers ~86% of cases inline.
297 for (unsigned t : targets)
298 affExprs.push_back(getAffineDimExpr(t, context));
299 AffineMap result = AffineMap::get(/*dimCount=*/numDims, /*symbolCount=*/0,
300 affExprs, context);
301 return result;
302}
303
304/// Creates an affine map each for each list of AffineExpr's in `exprsList`
305/// while inferring the right number of dimensional and symbolic inputs needed
306/// based on the maximum dimensional and symbolic identifier appearing in the
307/// expressions.
308template <typename AffineExprContainer>
311 MLIRContext *context) {
312 if (exprsList.empty())
313 return {};
314 int64_t maxDim = -1, maxSym = -1;
315 getMaxDimAndSymbol(exprsList, maxDim, maxSym);
317 maps.reserve(exprsList.size());
318 for (const auto &exprs : exprsList)
319 maps.push_back(AffineMap::get(/*dimCount=*/maxDim + 1,
320 /*symbolCount=*/maxSym + 1, exprs, context));
321 return maps;
322}
323
326 MLIRContext *context) {
327 return ::inferFromExprList(exprsList, context);
328}
329
332 MLIRContext *context) {
333 return ::inferFromExprList(exprsList, context);
334}
335
337 uint64_t gcd = 0;
338 for (AffineExpr resultExpr : getResults()) {
339 uint64_t thisGcd = resultExpr.getLargestKnownDivisor();
340 gcd = std::gcd(gcd, thisGcd);
341 }
342 if (gcd == 0)
343 gcd = std::numeric_limits<uint64_t>::max();
344 return gcd;
345}
346
348 MLIRContext *context) {
350 dimExprs.reserve(numDims);
351 for (unsigned i = 0; i < numDims; ++i)
352 dimExprs.push_back(mlir::getAffineDimExpr(i, context));
353 return get(/*dimCount=*/numDims, /*symbolCount=*/0, dimExprs, context);
354}
355
356MLIRContext *AffineMap::getContext() const { return map->context; }
357
359 if (getNumDims() != getNumResults())
360 return false;
362 for (unsigned i = 0, numDims = getNumDims(); i < numDims; ++i) {
363 auto expr = dyn_cast<AffineDimExpr>(results[i]);
364 if (!expr || expr.getPosition() != i)
365 return false;
366 }
367 return true;
368}
369
371 if (getNumSymbols() != getNumResults())
372 return false;
374 for (unsigned i = 0, numSymbols = getNumSymbols(); i < numSymbols; ++i) {
375 auto expr = dyn_cast<AffineDimExpr>(results[i]);
376 if (!expr || expr.getPosition() != i)
377 return false;
378 }
379 return true;
380}
381
382bool AffineMap::isEmpty() const {
383 return getNumDims() == 0 && getNumSymbols() == 0 && getNumResults() == 0;
384}
385
387 return getNumResults() == 1 && isa<AffineConstantExpr>(getResult(0));
388}
389
391 return llvm::all_of(getResults(), llvm::IsaPred<AffineConstantExpr>);
392}
393
395 assert(isSingleConstant() && "map must have a single constant result");
396 return cast<AffineConstantExpr>(getResult(0)).getValue();
397}
398
400 assert(isConstant() && "map must have only constant results");
402 for (auto expr : getResults())
403 result.emplace_back(cast<AffineConstantExpr>(expr).getValue());
404 return result;
405}
406
407unsigned AffineMap::getNumDims() const {
408 assert(map && "uninitialized map storage");
409 return map->numDims;
410}
411unsigned AffineMap::getNumSymbols() const {
412 assert(map && "uninitialized map storage");
413 return map->numSymbols;
414}
415unsigned AffineMap::getNumResults() const { return getResults().size(); }
416unsigned AffineMap::getNumInputs() const {
417 assert(map && "uninitialized map storage");
418 return map->numDims + map->numSymbols;
419}
421 assert(map && "uninitialized map storage");
422 return map->results();
423}
424AffineExpr AffineMap::getResult(unsigned idx) const {
425 return getResults()[idx];
426}
427
428unsigned AffineMap::getDimPosition(unsigned idx) const {
429 return cast<AffineDimExpr>(getResult(idx)).getPosition();
430}
431
432std::optional<unsigned> AffineMap::getResultPosition(AffineExpr input) const {
433 if (!isa<AffineDimExpr>(input))
434 return std::nullopt;
435
436 for (unsigned i = 0, numResults = getNumResults(); i < numResults; i++) {
437 if (getResult(i) == input)
438 return i;
439 }
440
441 return std::nullopt;
442}
443
444/// Folds the results of the application of an affine map on the provided
445/// operands to a constant if possible. Returns false if the folding happens,
446/// true otherwise.
447LogicalResult AffineMap::constantFold(ArrayRef<Attribute> operandConstants,
449 bool *hasPoison) const {
450 // Attempt partial folding.
452 partialConstantFold(operandConstants, &integers, hasPoison);
453
454 // If all expressions folded to a constant, populate results with attributes
455 // containing those constants.
456 if (integers.empty())
457 return failure();
458
459 auto range = llvm::map_range(integers, [this](int64_t i) {
460 return IntegerAttr::get(IndexType::get(getContext()), i);
461 });
462 results.append(range.begin(), range.end());
463 return success();
464}
465
468 bool *hasPoison) const {
469 assert(getNumInputs() == operandConstants.size());
470
471 // Fold each of the result expressions.
472 AffineExprConstantFolder exprFolder(getNumDims(), operandConstants);
474 exprs.reserve(getNumResults());
475
476 for (auto expr : getResults()) {
477 auto folded = exprFolder.constantFold(expr);
478 if (exprFolder.hasPoison() && hasPoison) {
479 *hasPoison = true;
480 return {};
481 }
482 // If did not fold to a constant, keep the original expression, and clear
483 // the integer results vector.
484 if (folded) {
485 exprs.push_back(
486 getAffineConstantExpr(folded.getInt(), folded.getContext()));
487 if (results)
488 results->push_back(folded.getInt());
489 } else {
490 exprs.push_back(expr);
491 if (results) {
492 results->clear();
493 results = nullptr;
494 }
495 }
496 }
497
498 return get(getNumDims(), getNumSymbols(), exprs, getContext());
499}
500
501/// Walk all of the AffineExpr's in this mapping. Each node in an expression
502/// tree is visited in postorder.
504 for (auto expr : getResults())
505 expr.walk(callback);
506}
507
508/// This method substitutes any uses of dimensions and symbols (e.g.
509/// dim#0 with dimReplacements[0]) in subexpressions and returns the modified
510/// expression mapping. Because this can be used to eliminate dims and
511/// symbols, the client needs to specify the number of dims and symbols in
512/// the result. The returned map always has the same number of results.
514 ArrayRef<AffineExpr> symReplacements,
515 unsigned numResultDims,
516 unsigned numResultSyms) const {
518 results.reserve(getNumResults());
519 for (auto expr : getResults())
520 results.push_back(
521 expr.replaceDimsAndSymbols(dimReplacements, symReplacements));
522 return get(numResultDims, numResultSyms, results, getContext());
523}
524
525/// Sparse replace method. Apply AffineExpr::replace(`expr`, `replacement`) to
526/// each of the results and return a new AffineMap with the new results and
527/// with the specified number of dims and symbols.
529 unsigned numResultDims,
530 unsigned numResultSyms) const {
532 newResults.reserve(getNumResults());
533 for (AffineExpr e : getResults())
534 newResults.push_back(e.replace(expr, replacement));
535 return AffineMap::get(numResultDims, numResultSyms, newResults, getContext());
536}
537
538/// Sparse replace method. Apply AffineExpr::replace(`map`) to each of the
539/// results and return a new AffineMap with the new results and with the
540/// specified number of dims and symbols.
542 unsigned numResultDims,
543 unsigned numResultSyms) const {
545 newResults.reserve(getNumResults());
546 for (AffineExpr e : getResults())
547 newResults.push_back(e.replace(map));
548 return AffineMap::get(numResultDims, numResultSyms, newResults, getContext());
549}
550
554 newResults.reserve(getNumResults());
555 for (AffineExpr e : getResults())
556 newResults.push_back(e.replace(map));
557 return AffineMap::inferFromExprList(newResults, getContext()).front();
558}
559
560AffineMap AffineMap::dropResults(const llvm::SmallBitVector &positions) const {
561 auto exprs = llvm::to_vector<4>(getResults());
562 // TODO: this is a pretty terrible API .. is there anything better?
563 for (auto pos = positions.find_last(); pos != -1;
564 pos = positions.find_prev(pos))
565 exprs.erase(exprs.begin() + pos);
566 return AffineMap::get(getNumDims(), getNumSymbols(), exprs, getContext());
567}
568
570 assert(getNumDims() == map.getNumResults() && "Number of results mismatch");
571 // Prepare `map` by concatenating the symbols and rewriting its exprs.
572 unsigned numDims = map.getNumDims();
573 unsigned numSymbolsThisMap = getNumSymbols();
574 unsigned numSymbols = numSymbolsThisMap + map.getNumSymbols();
575 SmallVector<AffineExpr, 8> newDims(numDims);
576 for (unsigned idx = 0; idx < numDims; ++idx) {
577 newDims[idx] = getAffineDimExpr(idx, getContext());
578 }
579 SmallVector<AffineExpr, 8> newSymbols(numSymbols - numSymbolsThisMap);
580 for (unsigned idx = numSymbolsThisMap; idx < numSymbols; ++idx) {
581 newSymbols[idx - numSymbolsThisMap] =
583 }
584 auto newMap =
585 map.replaceDimsAndSymbols(newDims, newSymbols, numDims, numSymbols);
587 exprs.reserve(getResults().size());
588 for (auto expr : getResults())
589 exprs.push_back(expr.compose(newMap));
590 return AffineMap::get(numDims, numSymbols, exprs, map.getContext());
591}
592
593// Inline size chosen empirically based on compilation profiling.
594// Profiled: 43.5M calls, avg=3.1+-2.3. N=8 covers ~98% of cases inline.
596 assert(getNumSymbols() == 0 && "Expected symbol-less map");
598 MLIRContext *ctx = getContext();
599 for (int64_t value : values)
600 exprs.push_back(getAffineConstantExpr(value, ctx));
602 res.reserve(getNumResults());
603 for (auto e : getResults())
604 res.push_back(cast<AffineConstantExpr>(e.replaceDims(exprs)).getValue());
605 return res;
606}
607
609 size_t res = 0;
610 for (auto expr : getResults()) {
611 auto constExpr = dyn_cast<AffineConstantExpr>(expr);
612 if (constExpr && constExpr.getValue() == 0)
613 res++;
614 }
615
616 return res;
617}
618
621
622 for (auto expr : getResults()) {
623 auto constExpr = dyn_cast<AffineConstantExpr>(expr);
624 if (!constExpr || constExpr.getValue() != 0)
625 newExprs.push_back(expr);
626 }
627 return AffineMap::get(getNumDims(), getNumSymbols(), newExprs, getContext());
628}
629
630bool AffineMap::isProjectedPermutation(bool allowZeroInResults) const {
631 if (getNumSymbols() > 0)
632 return false;
633
634 // Having more results than inputs means that results have duplicated dims or
635 // zeros that can't be mapped to input dims.
636 if (getNumResults() > getNumInputs())
637 return false;
638
639 SmallVector<bool, 8> seen(getNumInputs(), false);
640 // A projected permutation can have, at most, only one instance of each input
641 // dimension in the result expressions. Zeros are allowed as long as the
642 // number of result expressions is lower or equal than the number of input
643 // expressions.
644 for (auto expr : getResults()) {
645 if (auto dim = dyn_cast<AffineDimExpr>(expr)) {
646 if (seen[dim.getPosition()])
647 return false;
648 seen[dim.getPosition()] = true;
649 } else {
650 auto constExpr = dyn_cast<AffineConstantExpr>(expr);
651 if (!allowZeroInResults || !constExpr || constExpr.getValue() != 0)
652 return false;
653 }
654 }
655
656 // Results are either dims or zeros and zeros can be mapped to input dims.
657 return true;
658}
659
661 if (getNumDims() != getNumResults())
662 return false;
663 return isProjectedPermutation();
664}
665
668 exprs.reserve(resultPos.size());
669 for (auto idx : resultPos)
670 exprs.push_back(getResult(idx));
671 return AffineMap::get(getNumDims(), getNumSymbols(), exprs, getContext());
672}
673
674AffineMap AffineMap::getSliceMap(unsigned start, unsigned length) const {
676 getResults().slice(start, length), getContext());
677}
678
679AffineMap AffineMap::getMajorSubMap(unsigned numResults) const {
680 if (numResults == 0)
681 return AffineMap();
682 if (numResults > getNumResults())
683 return *this;
684 return getSliceMap(0, numResults);
685}
686
687AffineMap AffineMap::getMinorSubMap(unsigned numResults) const {
688 if (numResults == 0)
689 return AffineMap();
690 if (numResults > getNumResults())
691 return *this;
692 return getSliceMap(getNumResults() - numResults, numResults);
693}
694
695/// Implementation detail to compress multiple affine maps with a compressionFun
696/// that is expected to be either compressUnusedDims or compressUnusedSymbols.
697/// The implementation keeps track of num dims and symbols across the different
698/// affine maps.
701 llvm::function_ref<AffineMap(AffineMap)> compressionFun) {
702 if (maps.empty())
703 return SmallVector<AffineMap>();
705 allExprs.reserve(maps.size() * maps.front().getNumResults());
706 unsigned numDims = maps.front().getNumDims(),
707 numSymbols = maps.front().getNumSymbols();
708 for (auto m : maps) {
709 assert(numDims == m.getNumDims() && numSymbols == m.getNumSymbols() &&
710 "expected maps with same num dims and symbols");
711 llvm::append_range(allExprs, m.getResults());
712 }
713 AffineMap unifiedMap = compressionFun(
714 AffineMap::get(numDims, numSymbols, allExprs, maps.front().getContext()));
715 unsigned unifiedNumDims = unifiedMap.getNumDims(),
716 unifiedNumSymbols = unifiedMap.getNumSymbols();
717 ArrayRef<AffineExpr> unifiedResults = unifiedMap.getResults();
719 res.reserve(maps.size());
720 for (auto m : maps) {
721 res.push_back(AffineMap::get(unifiedNumDims, unifiedNumSymbols,
722 unifiedResults.take_front(m.getNumResults()),
723 m.getContext()));
724 unifiedResults = unifiedResults.drop_front(m.getNumResults());
725 }
726 return res;
727}
728
730 const llvm::SmallBitVector &unusedDims) {
731 return projectDims(map, unusedDims, /*compressDimsFlag=*/true);
732}
733
737
742
744 const llvm::SmallBitVector &unusedSymbols) {
745 return projectSymbols(map, unusedSymbols, /*compressSymbolsFlag=*/true);
746}
747
751
756
758 ArrayRef<OpFoldResult> operands,
759 SmallVector<Value> &remainingValues) {
760 SmallVector<AffineExpr> dimReplacements, symReplacements;
761 int64_t numDims = 0;
762 for (int64_t i = 0; i < map.getNumDims(); ++i) {
763 if (auto attr = dyn_cast<Attribute>(operands[i])) {
764 dimReplacements.push_back(
765 b.getAffineConstantExpr(cast<IntegerAttr>(attr).getInt()));
766 } else {
767 dimReplacements.push_back(b.getAffineDimExpr(numDims++));
768 remainingValues.push_back(cast<Value>(operands[i]));
769 }
770 }
771 int64_t numSymbols = 0;
772 for (int64_t i = 0; i < map.getNumSymbols(); ++i) {
773 if (auto attr = dyn_cast<Attribute>(operands[i + map.getNumDims()])) {
774 symReplacements.push_back(
775 b.getAffineConstantExpr(cast<IntegerAttr>(attr).getInt()));
776 } else {
777 symReplacements.push_back(b.getAffineSymbolExpr(numSymbols++));
778 remainingValues.push_back(cast<Value>(operands[i + map.getNumDims()]));
779 }
780 }
781 return map.replaceDimsAndSymbols(dimReplacements, symReplacements, numDims,
782 numSymbols);
783}
784
787 for (auto e : map.getResults()) {
788 exprs.push_back(
790 }
791 return AffineMap::get(map.getNumDims(), map.getNumSymbols(), exprs,
792 map.getContext());
793}
794
796 auto results = map.getResults();
797 SmallVector<AffineExpr, 4> uniqueExprs(results);
798 uniqueExprs.erase(llvm::unique(uniqueExprs), uniqueExprs.end());
799 return AffineMap::get(map.getNumDims(), map.getNumSymbols(), uniqueExprs,
800 map.getContext());
801}
802
804 if (map.isEmpty())
805 return map;
806 assert(map.getNumSymbols() == 0 && "expected map without symbols");
808 for (const auto &en : llvm::enumerate(map.getResults())) {
809 auto expr = en.value();
810 // Skip non-permutations.
811 if (auto d = dyn_cast<AffineDimExpr>(expr)) {
812 if (exprs[d.getPosition()])
813 continue;
814 exprs[d.getPosition()] = getAffineDimExpr(en.index(), d.getContext());
815 }
816 }
818 seenExprs.reserve(map.getNumDims());
819 for (auto expr : exprs)
820 if (expr)
821 seenExprs.push_back(expr);
822 if (seenExprs.size() != map.getNumInputs())
823 return AffineMap();
824 return AffineMap::get(map.getNumResults(), 0, seenExprs, map.getContext());
825}
826
828 assert(map.isProjectedPermutation(/*allowZeroInResults=*/true));
829 MLIRContext *context = map.getContext();
830 AffineExpr zero = mlir::getAffineConstantExpr(0, context);
831 // Start with all the results as 0.
832 SmallVector<AffineExpr, 4> exprs(map.getNumInputs(), zero);
833 for (unsigned i : llvm::seq(unsigned(0), map.getNumResults())) {
834 // Skip zeros from input map. 'exprs' is already initialized to zero.
835 if (auto constExpr = dyn_cast<AffineConstantExpr>(map.getResult(i))) {
836 assert(constExpr.getValue() == 0 &&
837 "Unexpected constant in projected permutation");
838 (void)constExpr;
839 continue;
840 }
841
842 // Reverse each dimension existing in the original map result.
843 exprs[map.getDimPosition(i)] = getAffineDimExpr(i, context);
844 }
845 return AffineMap::get(map.getNumResults(), /*symbolCount=*/0, exprs, context);
846}
847
849 MLIRContext *context) {
850 if (maps.empty())
851 return AffineMap::get(context);
852 unsigned numResults = 0, numDims = 0, numSymbols = 0;
853 for (auto m : maps)
854 numResults += m.getNumResults();
856 results.reserve(numResults);
857 for (auto m : maps) {
858 for (auto res : m.getResults())
859 results.push_back(res.shiftSymbols(m.getNumSymbols(), numSymbols));
860
861 numSymbols += m.getNumSymbols();
862 numDims = std::max(m.getNumDims(), numDims);
863 }
864 return AffineMap::get(numDims, numSymbols, results, context);
865}
866
867/// Common implementation to project out dimensions or symbols from an affine
868/// map based on the template type.
869/// Additionally, if 'compress' is true, the projected out dimensions or symbols
870/// are also dropped from the resulting map.
871template <typename AffineDimOrSymExpr>
873 const llvm::SmallBitVector &toProject,
874 bool compress) {
875 static_assert(llvm::is_one_of<AffineDimOrSymExpr, AffineDimExpr,
876 AffineSymbolExpr>::value,
877 "expected AffineDimExpr or AffineSymbolExpr");
878
879 constexpr bool isDim = std::is_same<AffineDimOrSymExpr, AffineDimExpr>::value;
880 int64_t numDimOrSym = (isDim) ? map.getNumDims() : map.getNumSymbols();
881 SmallVector<AffineExpr> replacements;
882 replacements.reserve(numDimOrSym);
883
884 auto createNewDimOrSym = (isDim) ? getAffineDimExpr : getAffineSymbolExpr;
885
886 using replace_fn_ty =
888 replace_fn_ty replaceDims = [](AffineExpr e,
889 ArrayRef<AffineExpr> replacements) {
890 return e.replaceDims(replacements);
891 };
892 replace_fn_ty replaceSymbols = [](AffineExpr e,
893 ArrayRef<AffineExpr> replacements) {
894 return e.replaceSymbols(replacements);
895 };
896 replace_fn_ty replaceNewDimOrSym = (isDim) ? replaceDims : replaceSymbols;
897
898 MLIRContext *context = map.getContext();
899 int64_t newNumDimOrSym = 0;
900 for (unsigned dimOrSym = 0; dimOrSym < numDimOrSym; ++dimOrSym) {
901 if (toProject.test(dimOrSym)) {
902 replacements.push_back(getAffineConstantExpr(0, context));
903 continue;
904 }
905 int64_t newPos = compress ? newNumDimOrSym++ : dimOrSym;
906 replacements.push_back(createNewDimOrSym(newPos, context));
907 }
908 SmallVector<AffineExpr> resultExprs;
909 resultExprs.reserve(map.getNumResults());
910 for (auto e : map.getResults())
911 resultExprs.push_back(replaceNewDimOrSym(e, replacements));
912
913 int64_t numDims = (compress && isDim) ? newNumDimOrSym : map.getNumDims();
914 int64_t numSyms = (compress && !isDim) ? newNumDimOrSym : map.getNumSymbols();
915 return AffineMap::get(numDims, numSyms, resultExprs, context);
916}
917
919 const llvm::SmallBitVector &projectedDimensions,
920 bool compressDimsFlag) {
921 return projectCommonImpl<AffineDimExpr>(map, projectedDimensions,
922 compressDimsFlag);
923}
924
926 const llvm::SmallBitVector &projectedSymbols,
927 bool compressSymbolsFlag) {
928 return projectCommonImpl<AffineSymbolExpr>(map, projectedSymbols,
929 compressSymbolsFlag);
930}
931
933 const llvm::SmallBitVector &projectedDimensions,
934 bool compressDimsFlag,
935 bool compressSymbolsFlag) {
936 map = projectDims(map, projectedDimensions, compressDimsFlag);
937 if (compressSymbolsFlag)
938 map = compressUnusedSymbols(map);
939 return map;
940}
941
943 unsigned numDims = maps[0].getNumDims();
944 llvm::SmallBitVector numDimsBitVector(numDims, true);
945 for (AffineMap m : maps) {
946 for (unsigned i = 0; i < numDims; ++i) {
947 if (m.isFunctionOfDim(i))
948 numDimsBitVector.reset(i);
949 }
950 }
951 return numDimsBitVector;
952}
953
955 unsigned numSymbols = maps[0].getNumSymbols();
956 llvm::SmallBitVector numSymbolsBitVector(numSymbols, true);
957 for (AffineMap m : maps) {
958 for (unsigned i = 0; i < numSymbols; ++i) {
959 if (m.isFunctionOfSymbol(i))
960 numSymbolsBitVector.reset(i);
961 }
962 }
963 return numSymbolsBitVector;
964}
965
968 const llvm::SmallBitVector &projectedDimensions) {
969 auto id = AffineMap::getMultiDimIdentityMap(rank, map.getContext());
970 AffineMap proj = id.dropResults(projectedDimensions);
971 return map.compose(proj);
972}
973
974//===----------------------------------------------------------------------===//
975// MutableAffineMap.
976//===----------------------------------------------------------------------===//
977
979 : results(map.getResults()), numDims(map.getNumDims()),
980 numSymbols(map.getNumSymbols()), context(map.getContext()) {}
981
983 results.clear();
984 numDims = map.getNumDims();
985 numSymbols = map.getNumSymbols();
986 context = map.getContext();
987 llvm::append_range(results, map.getResults());
988}
989
990bool MutableAffineMap::isMultipleOf(unsigned idx, int64_t factor) const {
991 return results[idx].isMultipleOf(factor);
992}
993
994// Simplifies the result affine expressions of this map. The expressions
995// have to be pure for the simplification implemented.
997 // Simplify each of the results if possible.
998 // TODO: functional-style map
999 for (unsigned i = 0, e = getNumResults(); i < e; i++) {
1000 results[i] = simplifyAffineExpr(getResult(i), numDims, numSymbols);
1001 }
1002}
1003
1005 return AffineMap::get(numDims, numSymbols, results, context);
1006}
return success()
static int64_t product(ArrayRef< int64_t > vals)
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`
static SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
A dimensional identifier appearing in an affine expression.
Definition AffineExpr.h:225
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:119
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:301
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:233
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:71
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...
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:699
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.
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:123
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:445
AffineMap getAffineMap() const
Get the AffineMap corresponding to this MutableAffineMap.
AffineExpr getResult(unsigned idx) const
Definition AffineMap.h:438
unsigned getNumSymbols() const
Definition AffineMap.h:443
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:437
unsigned getNumResults() const
Definition AffineMap.h:440
unsigned getNumDims() const
Definition AffineMap.h:441
void simplify()
Simplify the (result) expressions in this map using analysis (used by.