MLIR 24.0.0git
AffineExpr.cpp
Go to the documentation of this file.
1//===- AffineExpr.cpp - MLIR Affine Expr 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 <cmath>
10#include <cstdint>
11#include <utility>
12
13#include "AffineExprDetail.h"
14#include "mlir/IR/AffineExpr.h"
16#include "mlir/IR/AffineMap.h"
17#include "mlir/IR/IntegerSet.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/ADT/SmallVectorExtras.h"
20#include "llvm/Support/MathExtras.h"
21#include <numeric>
22#include <optional>
23
24using namespace mlir;
25using namespace mlir::detail;
26
27using llvm::divideCeilSigned;
28using llvm::divideFloorSigned;
29using llvm::divideSignedWouldOverflow;
30using llvm::mod;
31
32MLIRContext *AffineExpr::getContext() const { return expr->context; }
33
34AffineExprKind AffineExpr::getKind() const { return expr->kind; }
35
36/// Walk all of the AffineExprs in `e` in postorder. This is a private factory
37/// method to help handle lambda walk functions. Users should use the regular
38/// (non-static) `walk` method.
39template <typename WalkRetTy>
41 function_ref<WalkRetTy(AffineExpr)> callback) {
42 struct AffineExprWalker
43 : public AffineExprVisitor<AffineExprWalker, WalkRetTy> {
44 function_ref<WalkRetTy(AffineExpr)> callback;
45
46 AffineExprWalker(function_ref<WalkRetTy(AffineExpr)> callback)
47 : callback(callback) {}
48
49 WalkRetTy visitAffineBinaryOpExpr(AffineBinaryOpExpr expr) {
50 return callback(expr);
51 }
52 WalkRetTy visitConstantExpr(AffineConstantExpr expr) {
53 return callback(expr);
54 }
55 WalkRetTy visitDimExpr(AffineDimExpr expr) { return callback(expr); }
56 WalkRetTy visitSymbolExpr(AffineSymbolExpr expr) { return callback(expr); }
57 };
58
59 return AffineExprWalker(callback).walkPostOrder(e);
60}
61// Explicitly instantiate for the two supported return types.
63 function_ref<void(AffineExpr)> callback);
64template WalkResult
67
68// Dispatch affine expression construction based on kind.
71 if (kind == AffineExprKind::Add)
72 return lhs + rhs;
73 if (kind == AffineExprKind::Mul)
74 return lhs * rhs;
75 if (kind == AffineExprKind::FloorDiv)
76 return lhs.floorDiv(rhs);
77 if (kind == AffineExprKind::CeilDiv)
78 return lhs.ceilDiv(rhs);
79 if (kind == AffineExprKind::Mod)
80 return lhs % rhs;
81
82 llvm_unreachable("unknown binary operation on affine expressions");
83}
84
85/// This method substitutes any uses of dimensions and symbols (e.g.
86/// dim#0 with dimReplacements[0]) and returns the modified expression tree.
89 ArrayRef<AffineExpr> symReplacements) const {
90 switch (getKind()) {
92 return *this;
94 unsigned dimId = llvm::cast<AffineDimExpr>(*this).getPosition();
95 if (dimId >= dimReplacements.size())
96 return *this;
97 return dimReplacements[dimId];
98 }
100 unsigned symId = llvm::cast<AffineSymbolExpr>(*this).getPosition();
101 if (symId >= symReplacements.size())
102 return *this;
103 return symReplacements[symId];
104 }
110 auto binOp = llvm::cast<AffineBinaryOpExpr>(*this);
111 auto lhs = binOp.getLHS(), rhs = binOp.getRHS();
112 auto newLHS = lhs.replaceDimsAndSymbols(dimReplacements, symReplacements);
113 auto newRHS = rhs.replaceDimsAndSymbols(dimReplacements, symReplacements);
114 if (newLHS == lhs && newRHS == rhs)
115 return *this;
116 return getAffineBinaryOpExpr(getKind(), newLHS, newRHS);
117 }
118 llvm_unreachable("Unknown AffineExpr");
119}
120
122 return replaceDimsAndSymbols(dimReplacements, {});
123}
124
127 return replaceDimsAndSymbols({}, symReplacements);
128}
129
130/// Replace dims[offset ... numDims)
131/// by dims[offset + shift ... shift + numDims).
132AffineExpr AffineExpr::shiftDims(unsigned numDims, unsigned shift,
133 unsigned offset) const {
135 for (unsigned idx = 0; idx < offset; ++idx)
136 dims.push_back(getAffineDimExpr(idx, getContext()));
137 for (unsigned idx = offset; idx < numDims; ++idx)
138 dims.push_back(getAffineDimExpr(idx + shift, getContext()));
139 return replaceDimsAndSymbols(dims, {});
140}
141
142/// Replace symbols[offset ... numSymbols)
143/// by symbols[offset + shift ... shift + numSymbols).
144AffineExpr AffineExpr::shiftSymbols(unsigned numSymbols, unsigned shift,
145 unsigned offset) const {
147 for (unsigned idx = 0; idx < offset; ++idx)
148 symbols.push_back(getAffineSymbolExpr(idx, getContext()));
149 for (unsigned idx = offset; idx < numSymbols; ++idx)
150 symbols.push_back(getAffineSymbolExpr(idx + shift, getContext()));
151 return replaceDimsAndSymbols({}, symbols);
152}
153
154/// Sparse replace method. Return the modified expression tree.
157 auto it = map.find(*this);
158 if (it != map.end())
159 return it->second;
160 switch (getKind()) {
161 default:
162 return *this;
168 auto binOp = llvm::cast<AffineBinaryOpExpr>(*this);
169 auto lhs = binOp.getLHS(), rhs = binOp.getRHS();
170 auto newLHS = lhs.replace(map);
171 auto newRHS = rhs.replace(map);
172 if (newLHS == lhs && newRHS == rhs)
173 return *this;
174 return getAffineBinaryOpExpr(getKind(), newLHS, newRHS);
175 }
176 llvm_unreachable("Unknown AffineExpr");
177}
178
179/// Sparse replace method. Return the modified expression tree.
182 map.insert(std::make_pair(expr, replacement));
183 return replace(map);
184}
185/// Returns true if this expression is made out of only symbols and
186/// constants (no dimensional identifiers).
188 switch (getKind()) {
190 return true;
192 return false;
194 return true;
195
200 case AffineExprKind::Mod: {
201 auto expr = llvm::cast<AffineBinaryOpExpr>(*this);
202 return expr.getLHS().isSymbolicOrConstant() &&
203 expr.getRHS().isSymbolicOrConstant();
204 }
205 }
206 llvm_unreachable("Unknown AffineExpr");
207}
208
209/// Returns true if this is a pure affine expression, i.e., multiplication,
210/// floordiv, ceildiv, and mod is only allowed w.r.t constants.
212 switch (getKind()) {
216 return true;
217 case AffineExprKind::Add: {
218 auto op = llvm::cast<AffineBinaryOpExpr>(*this);
219 return op.getLHS().isPureAffine() && op.getRHS().isPureAffine();
220 }
221
222 case AffineExprKind::Mul: {
223 // TODO: Canonicalize the constants in binary operators to the RHS when
224 // possible, allowing this to merge into the next case.
225 auto op = llvm::cast<AffineBinaryOpExpr>(*this);
226 return op.getLHS().isPureAffine() && op.getRHS().isPureAffine() &&
227 (llvm::isa<AffineConstantExpr>(op.getLHS()) ||
228 llvm::isa<AffineConstantExpr>(op.getRHS()));
229 }
232 case AffineExprKind::Mod: {
233 auto op = llvm::cast<AffineBinaryOpExpr>(*this);
234 return op.getLHS().isPureAffine() &&
235 llvm::isa<AffineConstantExpr>(op.getRHS());
236 }
237 }
238 llvm_unreachable("Unknown AffineExpr");
239}
240
241// Returns the greatest known integral divisor of this affine expression.
243 AffineBinaryOpExpr binExpr(nullptr);
244 switch (getKind()) {
246 [[fallthrough]];
248 return 1;
250 [[fallthrough]];
252 // If the RHS is a constant and divides the known divisor on the LHS, the
253 // quotient is a known divisor of the expression.
254 binExpr = llvm::cast<AffineBinaryOpExpr>(*this);
255 auto rhs = llvm::dyn_cast<AffineConstantExpr>(binExpr.getRHS());
256 // Leave alone undefined expressions.
257 if (rhs && rhs.getValue() != 0) {
258 int64_t lhsDiv = binExpr.getLHS().getLargestKnownDivisor();
259 if (lhsDiv % rhs.getValue() == 0)
260 return std::abs(lhsDiv / rhs.getValue());
261 }
262 return 1;
263 }
265 return std::abs(llvm::cast<AffineConstantExpr>(*this).getValue());
266 case AffineExprKind::Mul: {
267 binExpr = llvm::cast<AffineBinaryOpExpr>(*this);
268 return binExpr.getLHS().getLargestKnownDivisor() *
269 binExpr.getRHS().getLargestKnownDivisor();
270 }
272 [[fallthrough]];
273 case AffineExprKind::Mod: {
274 binExpr = llvm::cast<AffineBinaryOpExpr>(*this);
275 return std::gcd((uint64_t)binExpr.getLHS().getLargestKnownDivisor(),
276 (uint64_t)binExpr.getRHS().getLargestKnownDivisor());
277 }
278 }
279 llvm_unreachable("Unknown AffineExpr");
280}
281
283 AffineBinaryOpExpr binExpr(nullptr);
284 uint64_t l, u;
285 switch (getKind()) {
287 [[fallthrough]];
289 return factor * factor == 1;
291 return llvm::cast<AffineConstantExpr>(*this).getValue() % factor == 0;
292 case AffineExprKind::Mul: {
293 binExpr = llvm::cast<AffineBinaryOpExpr>(*this);
294 // It's probably not worth optimizing this further (to not traverse the
295 // whole sub-tree under - it that would require a version of isMultipleOf
296 // that on a 'false' return also returns the largest known divisor).
297 return (l = binExpr.getLHS().getLargestKnownDivisor()) % factor == 0 ||
298 (u = binExpr.getRHS().getLargestKnownDivisor()) % factor == 0 ||
299 (l * u) % factor == 0;
300 }
304 case AffineExprKind::Mod: {
305 binExpr = llvm::cast<AffineBinaryOpExpr>(*this);
306 return std::gcd((uint64_t)binExpr.getLHS().getLargestKnownDivisor(),
307 (uint64_t)binExpr.getRHS().getLargestKnownDivisor()) %
308 factor ==
309 0;
310 }
311 }
312 llvm_unreachable("Unknown AffineExpr");
313}
314
315bool AffineExpr::isFunctionOfDim(unsigned position) const {
317 return *this == mlir::getAffineDimExpr(position, getContext());
318 }
319 if (auto expr = llvm::dyn_cast<AffineBinaryOpExpr>(*this)) {
320 return expr.getLHS().isFunctionOfDim(position) ||
321 expr.getRHS().isFunctionOfDim(position);
322 }
323 return false;
324}
325
326bool AffineExpr::isFunctionOfSymbol(unsigned position) const {
328 return *this == mlir::getAffineSymbolExpr(position, getContext());
329 }
330 if (auto expr = llvm::dyn_cast<AffineBinaryOpExpr>(*this)) {
331 return expr.getLHS().isFunctionOfSymbol(position) ||
332 expr.getRHS().isFunctionOfSymbol(position);
333 }
334 return false;
335}
336
340 return static_cast<ImplType *>(expr)->lhs;
341}
343 return static_cast<ImplType *>(expr)->rhs;
344}
345
348 return static_cast<ImplType *>(expr)->position;
349}
350
351/// Returns true if the expression is divisible by the given symbol with
352/// position `symbolPos`. The argument `opKind` specifies here what kind of
353/// division or mod operation called this division. It helps in implementing the
354/// commutative property of the floordiv and ceildiv operations. If the argument
355///`exprKind` is floordiv and `expr` is also a binary expression of a floordiv
356/// operation, then the commutative property can be used otherwise, the floordiv
357/// operation is not divisible. The same argument holds for ceildiv operation.
358static bool canSimplifyDivisionBySymbol(AffineExpr expr, unsigned symbolPos,
359 AffineExprKind opKind,
360 bool fromMul = false) {
361 // The argument `opKind` can either be Modulo, Floordiv or Ceildiv only.
362 assert((opKind == AffineExprKind::Mod || opKind == AffineExprKind::FloorDiv ||
363 opKind == AffineExprKind::CeilDiv) &&
364 "unexpected opKind");
365 switch (expr.getKind()) {
367 return cast<AffineConstantExpr>(expr).getValue() == 0;
369 return false;
371 return (cast<AffineSymbolExpr>(expr).getPosition() == symbolPos);
372 // Checks divisibility by the given symbol for both operands.
373 case AffineExprKind::Add: {
374 AffineBinaryOpExpr binaryExpr = cast<AffineBinaryOpExpr>(expr);
375 return canSimplifyDivisionBySymbol(binaryExpr.getLHS(), symbolPos,
376 opKind) &&
377 canSimplifyDivisionBySymbol(binaryExpr.getRHS(), symbolPos, opKind);
378 }
379 // Checks divisibility by the given symbol for both operands. Consider the
380 // expression `(((s1*s0) floordiv w) mod ((s1 * s2) floordiv p)) floordiv s1`,
381 // this is a division by s1 and both the operands of modulo are divisible by
382 // s1 but it is not divisible by s1 always. The third argument is
383 // `AffineExprKind::Mod` for this reason.
384 case AffineExprKind::Mod: {
385 AffineBinaryOpExpr binaryExpr = cast<AffineBinaryOpExpr>(expr);
386 return canSimplifyDivisionBySymbol(binaryExpr.getLHS(), symbolPos,
388 canSimplifyDivisionBySymbol(binaryExpr.getRHS(), symbolPos,
390 }
391 // Checks if any of the operand divisible by the given symbol.
392 case AffineExprKind::Mul: {
393 AffineBinaryOpExpr binaryExpr = cast<AffineBinaryOpExpr>(expr);
394 return canSimplifyDivisionBySymbol(binaryExpr.getLHS(), symbolPos, opKind,
395 true) ||
396 canSimplifyDivisionBySymbol(binaryExpr.getRHS(), symbolPos, opKind,
397 true);
398 }
399 // Floordiv and ceildiv are divisible by the given symbol when the first
400 // operand is divisible, and the affine expression kind of the argument expr
401 // is same as the argument `opKind`. This can be inferred from commutative
402 // property of floordiv and ceildiv operations and are as follow:
403 // (exp1 floordiv exp2) floordiv exp3 = (exp1 floordiv exp3) floordiv exp2
404 // (exp1 ceildiv exp2) ceildiv exp3 = (exp1 ceildiv exp3) ceildiv expr2
405 // It will fail 1.if operations are not same. For example:
406 // (exps1 ceildiv exp2) floordiv exp3 can not be simplified. 2.if there is a
407 // multiplication operation in the expression. For example:
408 // (exps1 ceildiv exp2) mul exp3 ceildiv exp4 can not be simplified.
411 AffineBinaryOpExpr binaryExpr = cast<AffineBinaryOpExpr>(expr);
412 if (opKind != expr.getKind())
413 return false;
414 if (fromMul)
415 return false;
416 return canSimplifyDivisionBySymbol(binaryExpr.getLHS(), symbolPos,
417 expr.getKind());
418 }
419 }
420 llvm_unreachable("Unknown AffineExpr");
421}
422
423/// Divides the given expression by the given symbol at position `symbolPos`. It
424/// considers the divisibility condition is checked before calling itself. A
425/// null expression is returned whenever the divisibility condition fails.
426static AffineExpr symbolicDivide(AffineExpr expr, unsigned symbolPos,
427 AffineExprKind opKind) {
428 // THe argument `opKind` can either be Modulo, Floordiv or Ceildiv only.
429 assert((opKind == AffineExprKind::Mod || opKind == AffineExprKind::FloorDiv ||
430 opKind == AffineExprKind::CeilDiv) &&
431 "unexpected opKind");
432 switch (expr.getKind()) {
434 if (cast<AffineConstantExpr>(expr).getValue() != 0)
435 return nullptr;
436 return getAffineConstantExpr(0, expr.getContext());
438 return nullptr;
440 return getAffineConstantExpr(1, expr.getContext());
441 // Dividing both operands by the given symbol.
442 case AffineExprKind::Add: {
443 AffineBinaryOpExpr binaryExpr = cast<AffineBinaryOpExpr>(expr);
445 expr.getKind(), symbolicDivide(binaryExpr.getLHS(), symbolPos, opKind),
446 symbolicDivide(binaryExpr.getRHS(), symbolPos, opKind));
447 }
448 // Dividing both operands by the given symbol.
449 case AffineExprKind::Mod: {
450 AffineBinaryOpExpr binaryExpr = cast<AffineBinaryOpExpr>(expr);
452 expr.getKind(),
453 symbolicDivide(binaryExpr.getLHS(), symbolPos, expr.getKind()),
454 symbolicDivide(binaryExpr.getRHS(), symbolPos, expr.getKind()));
455 }
456 // Dividing any of the operand by the given symbol.
457 case AffineExprKind::Mul: {
458 AffineBinaryOpExpr binaryExpr = cast<AffineBinaryOpExpr>(expr);
459 if (!canSimplifyDivisionBySymbol(binaryExpr.getLHS(), symbolPos, opKind))
460 return binaryExpr.getLHS() *
461 symbolicDivide(binaryExpr.getRHS(), symbolPos, opKind);
462 return symbolicDivide(binaryExpr.getLHS(), symbolPos, opKind) *
463 binaryExpr.getRHS();
464 }
465 // Dividing first operand only by the given symbol.
468 AffineBinaryOpExpr binaryExpr = cast<AffineBinaryOpExpr>(expr);
470 expr.getKind(),
471 symbolicDivide(binaryExpr.getLHS(), symbolPos, expr.getKind()),
472 binaryExpr.getRHS());
473 }
474 }
475 llvm_unreachable("Unknown AffineExpr");
476}
477
478/// Populate `result` with all summand operands of given (potentially nested)
479/// addition. If the given expression is not an addition, just populate the
480/// expression itself.
481/// Example: Add(Add(7, 8), Mul(9, 10)) will return [7, 8, Mul(9, 10)].
482static void getSummandExprs(AffineExpr expr, SmallVector<AffineExpr> &result) {
483 auto addExpr = dyn_cast<AffineBinaryOpExpr>(expr);
484 if (!addExpr || addExpr.getKind() != AffineExprKind::Add) {
485 result.push_back(expr);
486 return;
487 }
488 getSummandExprs(addExpr.getLHS(), result);
489 getSummandExprs(addExpr.getRHS(), result);
490}
491
492/// Return "true" if `candidate` is a negated expression, i.e., Mul(-1, expr).
493/// If so, also return the non-negated expression via `expr`.
494static bool isNegatedAffineExpr(AffineExpr candidate, AffineExpr &expr) {
495 auto mulExpr = dyn_cast<AffineBinaryOpExpr>(candidate);
496 if (!mulExpr || mulExpr.getKind() != AffineExprKind::Mul)
497 return false;
498 if (auto lhs = dyn_cast<AffineConstantExpr>(mulExpr.getLHS())) {
499 if (lhs.getValue() == -1) {
500 expr = mulExpr.getRHS();
501 return true;
502 }
503 }
504 if (auto rhs = dyn_cast<AffineConstantExpr>(mulExpr.getRHS())) {
505 if (rhs.getValue() == -1) {
506 expr = mulExpr.getLHS();
507 return true;
508 }
509 }
510 return false;
511}
512
513/// Return "true" if `lhs` % `rhs` is guaranteed to evaluate to zero based on
514/// the fact that `lhs` contains another modulo expression that ensures that
515/// `lhs` is divisible by `rhs`. This is a common pattern in the resulting IR
516/// after loop peeling.
517///
518/// Example: lhs = ub - ub % step
519/// rhs = step
520/// => (ub - ub % step) % step is guaranteed to evaluate to 0.
521static bool isModOfModSubtraction(AffineExpr lhs, AffineExpr rhs,
522 unsigned numDims, unsigned numSymbols) {
523 // TODO: Try to unify this function with `getBoundForAffineExpr`.
524 // Collect all summands in lhs.
526 getSummandExprs(lhs, summands);
527 // Look for Mul(-1, Mod(x, rhs)) among the summands. If x matches the
528 // remaining summands, then lhs % rhs is guaranteed to evaluate to 0.
529 for (int64_t i = 0, e = summands.size(); i < e; ++i) {
530 AffineExpr current = summands[i];
531 AffineExpr beforeNegation;
532 if (!isNegatedAffineExpr(current, beforeNegation))
533 continue;
534 AffineBinaryOpExpr innerMod = dyn_cast<AffineBinaryOpExpr>(beforeNegation);
535 if (!innerMod || innerMod.getKind() != AffineExprKind::Mod)
536 continue;
537 if (innerMod.getRHS() != rhs)
538 continue;
539 // Sum all remaining summands and subtract x. If that expression can be
540 // simplified to zero, then the remaining summands and x are equal.
541 AffineExpr diff = getAffineConstantExpr(0, lhs.getContext());
542 for (int64_t j = 0; j < e; ++j)
543 if (i != j)
544 diff = diff + summands[j];
545 diff = diff - innerMod.getLHS();
546 diff = simplifyAffineExpr(diff, numDims, numSymbols);
547 auto constExpr = dyn_cast<AffineConstantExpr>(diff);
548 if (constExpr && constExpr.getValue() == 0)
549 return true;
550 }
551 return false;
552}
553
554/// Simplify a semi-affine expression by handling modulo, floordiv, or ceildiv
555/// operations when the second operand simplifies to a symbol and the first
556/// operand is divisible by that symbol. It can be applied to any semi-affine
557/// expression. Returned expression can either be a semi-affine or pure affine
558/// expression.
559static AffineExpr simplifySemiAffine(AffineExpr expr, unsigned numDims,
560 unsigned numSymbols) {
561 switch (expr.getKind()) {
565 return expr;
567 case AffineExprKind::Mul: {
568 AffineBinaryOpExpr binaryExpr = cast<AffineBinaryOpExpr>(expr);
570 expr.getKind(),
571 simplifySemiAffine(binaryExpr.getLHS(), numDims, numSymbols),
572 simplifySemiAffine(binaryExpr.getRHS(), numDims, numSymbols));
573 }
574 // Check if the simplification of the second operand is a symbol, and the
575 // first operand is divisible by it. If the operation is a modulo, a constant
576 // zero expression is returned. In the case of floordiv and ceildiv, the
577 // symbol from the simplification of the second operand divides the first
578 // operand. Otherwise, simplification is not possible.
581 case AffineExprKind::Mod: {
582 AffineBinaryOpExpr binaryExpr = cast<AffineBinaryOpExpr>(expr);
583 AffineExpr sLHS =
584 simplifySemiAffine(binaryExpr.getLHS(), numDims, numSymbols);
585 AffineExpr sRHS =
586 simplifySemiAffine(binaryExpr.getRHS(), numDims, numSymbols);
587 if (isModOfModSubtraction(sLHS, sRHS, numDims, numSymbols))
588 return getAffineConstantExpr(0, expr.getContext());
589 AffineSymbolExpr symbolExpr = dyn_cast<AffineSymbolExpr>(
590 simplifySemiAffine(binaryExpr.getRHS(), numDims, numSymbols));
591 if (!symbolExpr)
592 return getAffineBinaryOpExpr(expr.getKind(), sLHS, sRHS);
593 unsigned symbolPos = symbolExpr.getPosition();
594 if (!canSimplifyDivisionBySymbol(binaryExpr.getLHS(), symbolPos,
595 expr.getKind()))
596 return getAffineBinaryOpExpr(expr.getKind(), sLHS, sRHS);
597 if (expr.getKind() == AffineExprKind::Mod)
598 return getAffineConstantExpr(0, expr.getContext());
599 AffineExpr simplifiedQuotient =
600 symbolicDivide(sLHS, symbolPos, expr.getKind());
601 return simplifiedQuotient
602 ? simplifiedQuotient
603 : getAffineBinaryOpExpr(expr.getKind(), sLHS, sRHS);
604 }
605 }
606 llvm_unreachable("Unknown AffineExpr");
607}
608
609static AffineExpr getAffineDimOrSymbol(AffineExprKind kind, unsigned position,
610 MLIRContext *context) {
611 auto assignCtx = [context](AffineDimExprStorage *storage) {
612 storage->context = context;
613 };
614
615 StorageUniquer &uniquer = context->getAffineUniquer();
616 return uniquer.get<AffineDimExprStorage>(
617 assignCtx, static_cast<unsigned>(kind), position);
618}
619
620AffineExpr mlir::getAffineDimExpr(unsigned position, MLIRContext *context) {
621 return getAffineDimOrSymbol(AffineExprKind::DimId, position, context);
622}
623
625 : AffineExpr(ptr) {}
626unsigned AffineSymbolExpr::getPosition() const {
627 return static_cast<ImplType *>(expr)->position;
628}
629
630AffineExpr mlir::getAffineSymbolExpr(unsigned position, MLIRContext *context) {
631 return getAffineDimOrSymbol(AffineExprKind::SymbolId, position, context);
632}
633
635 : AffineExpr(ptr) {}
636int64_t AffineConstantExpr::getValue() const {
637 return static_cast<ImplType *>(expr)->constant;
638}
639
640bool AffineExpr::operator==(int64_t v) const {
641 return *this == getAffineConstantExpr(v, getContext());
642}
643
644AffineExpr mlir::getAffineConstantExpr(int64_t constant, MLIRContext *context) {
645 auto assignCtx = [context](AffineConstantExprStorage *storage) {
646 storage->context = context;
647 };
648
649 StorageUniquer &uniquer = context->getAffineUniquer();
650 return uniquer.get<AffineConstantExprStorage>(assignCtx, constant);
651}
652
655 MLIRContext *context) {
656 return llvm::map_to_vector(constants, [&](int64_t constant) {
657 return getAffineConstantExpr(constant, context);
658 });
659}
660
661/// Simplify add expression. Return nullptr if it can't be simplified.
662static AffineExpr simplifyAdd(AffineExpr lhs, AffineExpr rhs) {
663 auto lhsConst = dyn_cast<AffineConstantExpr>(lhs);
664 auto rhsConst = dyn_cast<AffineConstantExpr>(rhs);
665 // Fold if both LHS, RHS are a constant and the sum does not overflow.
666 if (lhsConst && rhsConst) {
667 int64_t sum;
668 if (llvm::AddOverflow(lhsConst.getValue(), rhsConst.getValue(), sum)) {
669 return nullptr;
670 }
671 return getAffineConstantExpr(sum, lhs.getContext());
672 }
673
674 // Canonicalize so that only the RHS is a constant. (4 + d0 becomes d0 + 4).
675 // If only one of them is a symbolic expressions, make it the RHS.
676 if (isa<AffineConstantExpr>(lhs) ||
677 (lhs.isSymbolicOrConstant() && !rhs.isSymbolicOrConstant())) {
678 return rhs + lhs;
679 }
680
681 // At this point, if there was a constant, it would be on the right.
682
683 // Addition with a zero is a noop, return the other input.
684 if (rhsConst) {
685 if (rhsConst.getValue() == 0)
686 return lhs;
687 }
688 // Fold successive additions like (d0 + 2) + 3 into d0 + 5.
689 auto lBin = dyn_cast<AffineBinaryOpExpr>(lhs);
690 if (lBin && rhsConst && lBin.getKind() == AffineExprKind::Add) {
691 if (auto lrhs = dyn_cast<AffineConstantExpr>(lBin.getRHS()))
692 return lBin.getLHS() + (lrhs.getValue() + rhsConst.getValue());
693 }
694
695 // Detect "c1 * expr + c_2 * expr" as "(c1 + c2) * expr".
696 // c1 is rRhsConst, c2 is rLhsConst; firstExpr, secondExpr are their
697 // respective multiplicands.
698 std::optional<int64_t> rLhsConst, rRhsConst;
699 AffineExpr firstExpr, secondExpr;
700 AffineConstantExpr rLhsConstExpr;
701 auto lBinOpExpr = dyn_cast<AffineBinaryOpExpr>(lhs);
702 if (lBinOpExpr && lBinOpExpr.getKind() == AffineExprKind::Mul &&
703 (rLhsConstExpr = dyn_cast<AffineConstantExpr>(lBinOpExpr.getRHS()))) {
704 rLhsConst = rLhsConstExpr.getValue();
705 firstExpr = lBinOpExpr.getLHS();
706 } else {
707 rLhsConst = 1;
708 firstExpr = lhs;
709 }
710
711 auto rBinOpExpr = dyn_cast<AffineBinaryOpExpr>(rhs);
712 AffineConstantExpr rRhsConstExpr;
713 if (rBinOpExpr && rBinOpExpr.getKind() == AffineExprKind::Mul &&
714 (rRhsConstExpr = dyn_cast<AffineConstantExpr>(rBinOpExpr.getRHS()))) {
715 rRhsConst = rRhsConstExpr.getValue();
716 secondExpr = rBinOpExpr.getLHS();
717 } else {
718 rRhsConst = 1;
719 secondExpr = rhs;
720 }
721
722 if (rLhsConst && rRhsConst && firstExpr == secondExpr)
724 AffineExprKind::Mul, firstExpr,
725 getAffineConstantExpr(*rLhsConst + *rRhsConst, lhs.getContext()));
726
727 // When doing successive additions, bring constant to the right: turn (d0 + 2)
728 // + d1 into (d0 + d1) + 2.
729 if (lBin && lBin.getKind() == AffineExprKind::Add) {
730 if (auto lrhs = dyn_cast<AffineConstantExpr>(lBin.getRHS())) {
731 return lBin.getLHS() + rhs + lrhs;
732 }
733 }
734
735 // Detect and transform "expr - q * (expr floordiv q)" to "expr mod q", where
736 // q may be a constant or symbolic expression. This leads to a much more
737 // efficient form when 'c' is a power of two, and in general a more compact
738 // and readable form.
739
740 // Process '(expr floordiv c) * (-c)'.
741 if (!rBinOpExpr || rBinOpExpr.getKind() != AffineExprKind::Mul)
742 return nullptr;
743
744 auto lrhs = rBinOpExpr.getLHS();
745 auto rrhs = rBinOpExpr.getRHS();
746
747 AffineExpr llrhs, rlrhs;
748
749 // Check if lrhsBinOpExpr is of the form (expr floordiv q) * q,
750 // where q is a symbolic expression.
751 auto lrhsBinOpExpr = dyn_cast<AffineBinaryOpExpr>(lrhs);
752 // Check rrhsConstOpExpr = -1 as part of ((expr floordiv q) * q)) * (-1).
753 auto rrhsConstOpExpr = dyn_cast<AffineConstantExpr>(rrhs);
754 if (rrhsConstOpExpr && rrhsConstOpExpr.getValue() == -1 && lrhsBinOpExpr &&
755 lrhsBinOpExpr.getKind() == AffineExprKind::Mul) {
756 // Check llrhs = expr floordiv q.
757 llrhs = lrhsBinOpExpr.getLHS();
758 // Check rlrhs = q.
759 rlrhs = lrhsBinOpExpr.getRHS();
760 auto llrhsBinOpExpr = dyn_cast<AffineBinaryOpExpr>(llrhs);
761 if (!llrhsBinOpExpr || llrhsBinOpExpr.getKind() != AffineExprKind::FloorDiv)
762 return nullptr;
763 if (llrhsBinOpExpr.getRHS() == rlrhs && lhs == llrhsBinOpExpr.getLHS())
764 return lhs % rlrhs;
765 }
766
767 // Process lrhs, which is 'expr floordiv c'.
768 // expr + (expr // c * -c) = expr % c
769 AffineBinaryOpExpr lrBinOpExpr = dyn_cast<AffineBinaryOpExpr>(lrhs);
770 if (!lrBinOpExpr || rhs.getKind() != AffineExprKind::Mul ||
771 lrBinOpExpr.getKind() != AffineExprKind::FloorDiv)
772 return nullptr;
773
774 llrhs = lrBinOpExpr.getLHS();
775 rlrhs = lrBinOpExpr.getRHS();
776 auto rlrhsConstOpExpr = dyn_cast<AffineConstantExpr>(rlrhs);
777 // We don't support modulo with a negative RHS.
778 bool isPositiveRhs = rlrhsConstOpExpr && rlrhsConstOpExpr.getValue() > 0;
779
780 if (isPositiveRhs && lhs == llrhs && rlrhs == -rrhs) {
781 return lhs % rlrhs;
782 }
783
784 // Try simplify lhs's last operand with rhs. e.g:
785 // (s0 * 64 + s1) + (s1 // c * -c) --->
786 // s0 * 64 + (s1 + s1 // c * -c) -->
787 // s0 * 64 + s1 % c
788 if (lBinOpExpr && lBinOpExpr.getKind() == AffineExprKind::Add) {
789 if (auto simplified = simplifyAdd(lBinOpExpr.getRHS(), rhs))
790 return lBinOpExpr.getLHS() + simplified;
791 }
792 return nullptr;
793}
794
795/// Get the canonical order of two commutative exprs arguments.
796static std::pair<AffineExpr, AffineExpr>
797orderCommutativeArgs(AffineExpr expr1, AffineExpr expr2) {
798 auto sym1 = dyn_cast<AffineSymbolExpr>(expr1);
799 auto sym2 = dyn_cast<AffineSymbolExpr>(expr2);
800 // Try to order by symbol/dim position first.
801 if (sym1 && sym2)
802 return sym1.getPosition() < sym2.getPosition() ? std::pair{expr1, expr2}
803 : std::pair{expr2, expr1};
804
805 auto dim1 = dyn_cast<AffineDimExpr>(expr1);
806 auto dim2 = dyn_cast<AffineDimExpr>(expr2);
807 if (dim1 && dim2)
808 return dim1.getPosition() < dim2.getPosition() ? std::pair{expr1, expr2}
809 : std::pair{expr2, expr1};
810
811 // Put dims before symbols.
812 if (dim1 && sym2)
813 return {dim1, sym2};
814
815 if (sym1 && dim2)
816 return {dim2, sym1};
817
818 // Otherwise, keep original order.
819 return {expr1, expr2};
820}
821
822AffineExpr AffineExpr::operator+(int64_t v) const {
823 return *this + getAffineConstantExpr(v, getContext());
824}
826 if (auto simplified = simplifyAdd(*this, other))
827 return simplified;
828
829 auto [lhs, rhs] = orderCommutativeArgs(*this, other);
830
831 StorageUniquer &uniquer = getContext()->getAffineUniquer();
832 return uniquer.get<AffineBinaryOpExprStorage>(
833 /*initFn=*/{}, static_cast<unsigned>(AffineExprKind::Add), lhs, rhs);
834}
835
836/// Simplify a multiply expression. Return nullptr if it can't be simplified.
838 auto lhsConst = dyn_cast<AffineConstantExpr>(lhs);
839 auto rhsConst = dyn_cast<AffineConstantExpr>(rhs);
840
841 if (lhsConst && rhsConst) {
843 if (llvm::MulOverflow(lhsConst.getValue(), rhsConst.getValue(), product)) {
844 return nullptr;
845 }
846 return getAffineConstantExpr(product, lhs.getContext());
847 }
848
849 if (!lhs.isSymbolicOrConstant() && !rhs.isSymbolicOrConstant())
850 return nullptr;
851
852 // Canonicalize the mul expression so that the constant/symbolic term is the
853 // RHS. If both the lhs and rhs are symbolic, swap them if the lhs is a
854 // constant. (Note that a constant is trivially symbolic).
855 if (!rhs.isSymbolicOrConstant() || isa<AffineConstantExpr>(lhs)) {
856 // At least one of them has to be symbolic.
857 return rhs * lhs;
858 }
859
860 // At this point, if there was a constant, it would be on the right.
861
862 // Multiplication with a one is a noop, return the other input.
863 if (rhsConst) {
864 if (rhsConst.getValue() == 1)
865 return lhs;
866 // Multiplication with zero.
867 if (rhsConst.getValue() == 0)
868 return rhsConst;
869 }
870
871 // Fold successive multiplications: eg: (d0 * 2) * 3 into d0 * 6.
872 auto lBin = dyn_cast<AffineBinaryOpExpr>(lhs);
873 if (lBin && rhsConst && lBin.getKind() == AffineExprKind::Mul) {
874 if (auto lrhs = dyn_cast<AffineConstantExpr>(lBin.getRHS()))
875 return lBin.getLHS() * (lrhs.getValue() * rhsConst.getValue());
876 }
877
878 // When doing successive multiplication, bring constant to the right: turn (d0
879 // * 2) * d1 into (d0 * d1) * 2.
880 if (lBin && lBin.getKind() == AffineExprKind::Mul) {
881 if (auto lrhs = dyn_cast<AffineConstantExpr>(lBin.getRHS())) {
882 return (lBin.getLHS() * rhs) * lrhs;
883 }
884 }
885
886 return nullptr;
887}
888
893 if (auto simplified = simplifyMul(*this, other))
894 return simplified;
895
896 auto [lhs, rhs] = orderCommutativeArgs(*this, other);
897
899 return uniquer.get<AffineBinaryOpExprStorage>(
900 /*initFn=*/{}, static_cast<unsigned>(AffineExprKind::Mul), lhs, rhs);
901}
902
903// Unary minus, delegate to operator*.
905 return *this * getAffineConstantExpr(-1, getContext());
906}
907
908// Delegate to operator+.
910 // Use unsigned negation to avoid signed integer overflow for INT64_MIN.
911 return *this + static_cast<int64_t>(-static_cast<uint64_t>(v));
912}
914 if (auto constOther = dyn_cast<AffineConstantExpr>(other))
915 return *this - constOther.getValue();
916 return *this + (-other);
917}
918
920 auto rhsConst = dyn_cast<AffineConstantExpr>(rhs);
921
922 // For the defined cases, simplify x floordiv x is 1.
923 if (lhs == rhs && (!rhsConst || rhsConst.getValue() >= 1))
924 return getAffineConstantExpr(1, lhs.getContext());
925
926 // All other simplifications further below are for the RHS constant case.
927 if (!rhsConst || rhsConst.getValue() == 0)
928 return nullptr;
929
930 if (auto lhsConst = dyn_cast<AffineConstantExpr>(lhs)) {
931 if (divideSignedWouldOverflow(lhsConst.getValue(), rhsConst.getValue()))
932 return nullptr;
934 divideFloorSigned(lhsConst.getValue(), rhsConst.getValue()),
935 lhs.getContext());
936 }
937
938 // Fold floordiv of a multiply with a constant that is a multiple of the
939 // divisor. Eg: (i * 128) floordiv 64 = i * 2.
940 if (rhsConst == 1)
941 return lhs;
942
943 // Simplify `(expr * lrhs) floordiv rhsConst` when `lrhs` is known to be a
944 // multiple of `rhsConst`.
945 auto lBin = dyn_cast<AffineBinaryOpExpr>(lhs);
946 if (lBin && lBin.getKind() == AffineExprKind::Mul) {
947 if (auto lrhs = dyn_cast<AffineConstantExpr>(lBin.getRHS())) {
948 // `rhsConst` is known to be a nonzero constant.
949 if (lrhs.getValue() % rhsConst.getValue() == 0)
950 return lBin.getLHS() * (lrhs.getValue() / rhsConst.getValue());
951 }
952 }
953
954 // Simplify (expr1 + expr2) floordiv divConst when either expr1 or expr2 is
955 // known to be a multiple of divConst.
956 if (lBin && lBin.getKind() == AffineExprKind::Add) {
957 int64_t llhsDiv = lBin.getLHS().getLargestKnownDivisor();
958 int64_t lrhsDiv = lBin.getRHS().getLargestKnownDivisor();
959 // rhsConst is known to be a nonzero constant.
960 if (llhsDiv % rhsConst.getValue() == 0 ||
961 lrhsDiv % rhsConst.getValue() == 0)
962 return lBin.getLHS().floorDiv(rhsConst.getValue()) +
963 lBin.getRHS().floorDiv(rhsConst.getValue());
964 }
965
966 return nullptr;
967}
968
971}
973 if (auto simplified = simplifyFloorDiv(*this, other))
974 return simplified;
975
977 return uniquer.get<AffineBinaryOpExprStorage>(
978 /*initFn=*/{}, static_cast<unsigned>(AffineExprKind::FloorDiv), *this,
979 other);
980}
981
983 auto rhsConst = dyn_cast<AffineConstantExpr>(rhs);
984
985 // For the defined cases, simplify x ceildiv x is 1.
986 if (lhs == rhs && (!rhsConst || rhsConst.getValue() >= 1))
987 return getAffineConstantExpr(1, lhs.getContext());
988
989 // All other simplifications further below are for the RHS constant case.
990 if (!rhsConst || rhsConst.getValue() == 0)
991 return nullptr;
992
993 if (auto lhsConst = dyn_cast<AffineConstantExpr>(lhs)) {
994 if (divideSignedWouldOverflow(lhsConst.getValue(), rhsConst.getValue()))
995 return nullptr;
997 divideCeilSigned(lhsConst.getValue(), rhsConst.getValue()),
998 lhs.getContext());
999 }
1000
1001 // Fold ceildiv of a multiply with a constant that is a multiple of the
1002 // divisor. Eg: (i * 128) ceildiv 64 = i * 2.
1003 if (rhsConst.getValue() == 1)
1004 return lhs;
1005
1006 // Simplify `(expr * lrhs) ceildiv rhsConst` when `lrhs` is known to be a
1007 // multiple of `rhsConst`.
1008 auto lBin = dyn_cast<AffineBinaryOpExpr>(lhs);
1009 if (lBin && lBin.getKind() == AffineExprKind::Mul) {
1010 if (auto lrhs = dyn_cast<AffineConstantExpr>(lBin.getRHS())) {
1011 // `rhsConst` is known to be a nonzero constant.
1012 if (lrhs.getValue() % rhsConst.getValue() == 0)
1013 return lBin.getLHS() * (lrhs.getValue() / rhsConst.getValue());
1014 }
1015 }
1016
1017 return nullptr;
1018}
1019
1022}
1024 if (auto simplified = simplifyCeilDiv(*this, other))
1025 return simplified;
1026
1028 return uniquer.get<AffineBinaryOpExprStorage>(
1029 /*initFn=*/{}, static_cast<unsigned>(AffineExprKind::CeilDiv), *this,
1030 other);
1031}
1032
1034 auto rhsConst = dyn_cast<AffineConstantExpr>(rhs);
1035
1036 // For the defined cases, simplify x % x to 0.
1037 if (lhs == rhs && (!rhsConst || rhsConst.getValue() >= 1))
1038 return getAffineConstantExpr(0, lhs.getContext());
1039
1040 // mod w.r.t zero or negative numbers is undefined and preserved as is.
1041 // All other simplifications further below are for the RHS constant case.
1042 if (!rhsConst || rhsConst.getValue() < 1)
1043 return nullptr;
1044
1045 if (auto lhsConst = dyn_cast<AffineConstantExpr>(lhs)) {
1046 // mod never overflows.
1047 return getAffineConstantExpr(mod(lhsConst.getValue(), rhsConst.getValue()),
1048 lhs.getContext());
1049 }
1050
1051 // Fold modulo of an expression that is known to be a multiple of a constant
1052 // to zero if that constant is a multiple of the modulo factor. Eg: (i * 128)
1053 // mod 64 is folded to 0, and less trivially, (i*(j*4*(k*32))) mod 128 = 0.
1054 if (lhs.getLargestKnownDivisor() % rhsConst.getValue() == 0)
1055 return getAffineConstantExpr(0, lhs.getContext());
1056
1057 // Simplify (expr1 + expr2) mod divConst when either expr1 or expr2 is
1058 // known to be a multiple of divConst.
1059 auto lBin = dyn_cast<AffineBinaryOpExpr>(lhs);
1060 if (lBin && lBin.getKind() == AffineExprKind::Add) {
1061 int64_t llhsDiv = lBin.getLHS().getLargestKnownDivisor();
1062 int64_t lrhsDiv = lBin.getRHS().getLargestKnownDivisor();
1063 // rhsConst is known to be a positive constant.
1064 if (llhsDiv % rhsConst.getValue() == 0)
1065 return lBin.getRHS() % rhsConst.getValue();
1066 if (lrhsDiv % rhsConst.getValue() == 0)
1067 return lBin.getLHS() % rhsConst.getValue();
1068 }
1069
1070 // Simplify (e % a) % b to e % b when b evenly divides a
1071 if (lBin && lBin.getKind() == AffineExprKind::Mod) {
1072 auto intermediate = dyn_cast<AffineConstantExpr>(lBin.getRHS());
1073 if (intermediate && intermediate.getValue() >= 1 &&
1074 mod(intermediate.getValue(), rhsConst.getValue()) == 0) {
1075 return lBin.getLHS() % rhsConst.getValue();
1076 }
1077 }
1078
1079 return nullptr;
1080}
1081
1083 return *this % getAffineConstantExpr(v, getContext());
1084}
1086 if (auto simplified = simplifyMod(*this, other))
1087 return simplified;
1088
1090 return uniquer.get<AffineBinaryOpExprStorage>(
1091 /*initFn=*/{}, static_cast<unsigned>(AffineExprKind::Mod), *this, other);
1092}
1093
1095 SmallVector<AffineExpr, 8> dimReplacements(map.getResults());
1096 return replaceDimsAndSymbols(dimReplacements, {});
1097}
1099 expr.print(os);
1100 return os;
1101}
1102
1103/// Constructs an affine expression from a flat ArrayRef. If there are local
1104/// identifiers (neither dimensional nor symbolic) that appear in the sum of
1105/// products expression, `localExprs` is expected to have the AffineExpr
1106/// for it, and is substituted into. The ArrayRef `flatExprs` is expected to be
1107/// in the format [dims, symbols, locals, constant term].
1109 unsigned numDims,
1110 unsigned numSymbols,
1111 ArrayRef<AffineExpr> localExprs,
1112 MLIRContext *context) {
1113 // Assert expected numLocals = flatExprs.size() - numDims - numSymbols - 1.
1114 assert(flatExprs.size() - numDims - numSymbols - 1 == localExprs.size() &&
1115 "unexpected number of local expressions");
1116
1117 auto expr = getAffineConstantExpr(0, context);
1118 // Dimensions and symbols.
1119 for (unsigned j = 0; j < numDims + numSymbols; j++) {
1120 if (flatExprs[j] == 0)
1121 continue;
1122 auto id = j < numDims ? getAffineDimExpr(j, context)
1123 : getAffineSymbolExpr(j - numDims, context);
1124 expr = expr + id * flatExprs[j];
1125 }
1126
1127 // Local identifiers.
1128 for (unsigned j = numDims + numSymbols, e = flatExprs.size() - 1; j < e;
1129 j++) {
1130 if (flatExprs[j] == 0)
1131 continue;
1132 auto term = localExprs[j - numDims - numSymbols] * flatExprs[j];
1133 expr = expr + term;
1134 }
1135
1136 // Constant term.
1137 int64_t constTerm = flatExprs[flatExprs.size() - 1];
1138 if (constTerm != 0)
1139 expr = expr + constTerm;
1140 return expr;
1141}
1142
1143/// Constructs a semi-affine expression from a flat ArrayRef. If there are
1144/// local identifiers (neither dimensional nor symbolic) that appear in the sum
1145/// of products expression, `localExprs` is expected to have the AffineExprs for
1146/// it, and is substituted into. The ArrayRef `flatExprs` is expected to be in
1147/// the format [dims, symbols, locals, constant term]. The semi-affine
1148/// expression is constructed in the sorted order of dimension and symbol
1149/// position numbers. Note: local expressions/ids are used for mod, div as well
1150/// as symbolic RHS terms for terms that are not pure affine.
1152 unsigned numDims,
1153 unsigned numSymbols,
1154 ArrayRef<AffineExpr> localExprs,
1155 MLIRContext *context) {
1156 assert(!flatExprs.empty() && "flatExprs cannot be empty");
1157
1158 // Assert expected numLocals = flatExprs.size() - numDims - numSymbols - 1.
1159 assert(flatExprs.size() - numDims - numSymbols - 1 == localExprs.size() &&
1160 "unexpected number of local expressions");
1161
1162 AffineExpr expr = getAffineConstantExpr(0, context);
1163
1164 // We design indices as a pair which help us present the semi-affine map as
1165 // sum of product where terms are sorted based on dimension or symbol
1166 // position: <keyA, keyB> for expressions of the form dimension * symbol,
1167 // where keyA is the position number of the dimension and keyB is the
1168 // position number of the symbol. For dimensional expressions we set the index
1169 // as (position number of the dimension, -1), as we want dimensional
1170 // expressions to appear before symbolic and product of dimensional and
1171 // symbolic expressions having the dimension with the same position number.
1172 // For symbolic expression set the index as (position number of the symbol,
1173 // maximum of last dimension and symbol position) number. For example, we want
1174 // the expression we are constructing to look something like: d0 + d0 * s0 +
1175 // s0 + d1*s1 + s1.
1176
1177 // Stores the affine expression corresponding to a given index.
1179 // Stores the constant coefficient value corresponding to a given
1180 // dimension, symbol or a non-pure affine expression stored in `localExprs`.
1182 // Stores the indices as defined above, and later sorted to produce
1183 // the semi-affine expression in the desired form.
1185
1186 // Example: expression = d0 + d0 * s0 + 2 * s0.
1187 // indices = [{0,-1}, {0, 0}, {0, 1}]
1188 // coefficients = [{{0, -1}, 1}, {{0, 0}, 1}, {{0, 1}, 2}]
1189 // indexToExprMap = [{{0, -1}, d0}, {{0, 0}, d0 * s0}, {{0, 1}, s0}]
1190
1191 // Adds entries to `indexToExprMap`, `coefficients` and `indices`.
1192 auto addEntry = [&](std::pair<unsigned, signed> index, int64_t coefficient,
1193 AffineExpr expr) {
1194 assert(!llvm::is_contained(indices, index) &&
1195 "Key is already present in indices vector and overwriting will "
1196 "happen in `indexToExprMap` and `coefficients`!");
1197
1198 indices.push_back(index);
1199 coefficients.insert({index, coefficient});
1200 indexToExprMap.insert({index, expr});
1201 };
1202
1203 // Design indices for dimensional or symbolic terms, and store the indices,
1204 // constant coefficient corresponding to the indices in `coefficients` map,
1205 // and affine expression corresponding to indices in `indexToExprMap` map.
1206
1207 // Ensure we do not have duplicate keys in `indexToExpr` map.
1208 unsigned offsetSym = 0;
1209 signed offsetDim = -1;
1210 for (unsigned j = numDims; j < numDims + numSymbols; ++j) {
1211 if (flatExprs[j] == 0)
1212 continue;
1213 // For symbolic expression set the index as <position number
1214 // of the symbol, max(dimCount, symCount)> number,
1215 // as we want symbolic expressions with the same positional number to
1216 // appear after dimensional expressions having the same positional number.
1217 std::pair<unsigned, signed> indexEntry(
1218 j - numDims, std::max(numDims, numSymbols) + offsetSym++);
1219 addEntry(indexEntry, flatExprs[j],
1220 getAffineSymbolExpr(j - numDims, context));
1221 }
1222
1223 // Denotes semi-affine product, modulo or division terms, which has been added
1224 // to the `indexToExpr` map.
1225 SmallVector<bool, 4> addedToMap(flatExprs.size() - numDims - numSymbols - 1,
1226 false);
1227 unsigned lhsPos, rhsPos;
1228 // Construct indices for product terms involving dimension, symbol or constant
1229 // as lhs/rhs, and store the indices, constant coefficient corresponding to
1230 // the indices in `coefficients` map, and affine expression corresponding to
1231 // in indices in `indexToExprMap` map.
1232 for (const auto &it : llvm::enumerate(localExprs)) {
1233 if (flatExprs[numDims + numSymbols + it.index()] == 0)
1234 continue;
1235 AffineExpr expr = it.value();
1236 auto binaryExpr = dyn_cast<AffineBinaryOpExpr>(expr);
1237 if (!binaryExpr)
1238 continue;
1239
1240 AffineExpr lhs = binaryExpr.getLHS();
1241 AffineExpr rhs = binaryExpr.getRHS();
1242 if (!((isa<AffineDimExpr>(lhs) || isa<AffineSymbolExpr>(lhs)) &&
1243 (isa<AffineDimExpr>(rhs) || isa<AffineSymbolExpr>(rhs) ||
1244 isa<AffineConstantExpr>(rhs)))) {
1245 continue;
1246 }
1247 if (isa<AffineConstantExpr>(rhs)) {
1248 // For product/modulo/division expressions, when rhs of modulo/division
1249 // expression is constant, we put 0 in place of keyB, because we want
1250 // them to appear earlier in the semi-affine expression we are
1251 // constructing. When rhs is constant, we place 0 in place of keyB.
1252 if (isa<AffineDimExpr>(lhs)) {
1253 lhsPos = cast<AffineDimExpr>(lhs).getPosition();
1254 std::pair<unsigned, signed> indexEntry(lhsPos, offsetDim--);
1255 addEntry(indexEntry, flatExprs[numDims + numSymbols + it.index()],
1256 expr);
1257 } else {
1258 lhsPos = cast<AffineSymbolExpr>(lhs).getPosition();
1259 std::pair<unsigned, signed> indexEntry(
1260 lhsPos, std::max(numDims, numSymbols) + offsetSym++);
1261 addEntry(indexEntry, flatExprs[numDims + numSymbols + it.index()],
1262 expr);
1263 }
1264 } else if (isa<AffineDimExpr>(lhs)) {
1265 // For product/modulo/division expressions having lhs as dimension and rhs
1266 // as symbol, we order the terms in the semi-affine expression based on
1267 // the pair: <keyA, keyB> for expressions of the form dimension * symbol,
1268 // where keyA is the position number of the dimension and keyB is the
1269 // position number of the symbol.
1270 lhsPos = cast<AffineDimExpr>(lhs).getPosition();
1271 rhsPos = cast<AffineSymbolExpr>(rhs).getPosition();
1272 std::pair<unsigned, signed> indexEntry(lhsPos, rhsPos);
1273 addEntry(indexEntry, flatExprs[numDims + numSymbols + it.index()], expr);
1274 } else {
1275 // For product/modulo/division expressions having both lhs and rhs as
1276 // symbol, we design indices as a pair: <keyA, keyB> for expressions
1277 // of the form dimension * symbol, where keyA is the position number of
1278 // the dimension and keyB is the position number of the symbol.
1279 lhsPos = cast<AffineSymbolExpr>(lhs).getPosition();
1280 rhsPos = cast<AffineSymbolExpr>(rhs).getPosition();
1281 std::pair<unsigned, signed> indexEntry(
1282 lhsPos, std::max(numDims, numSymbols) + offsetSym++);
1283 addEntry(indexEntry, flatExprs[numDims + numSymbols + it.index()], expr);
1284 }
1285 addedToMap[it.index()] = true;
1286 }
1287
1288 for (unsigned j = 0; j < numDims; ++j) {
1289 if (flatExprs[j] == 0)
1290 continue;
1291 // For dimensional expressions we set the index as <position number of the
1292 // dimension, 0>, as we want dimensional expressions to appear before
1293 // symbolic ones and products of dimensional and symbolic expressions
1294 // having the dimension with the same position number.
1295 std::pair<unsigned, signed> indexEntry(j, offsetDim--);
1296 addEntry(indexEntry, flatExprs[j], getAffineDimExpr(j, context));
1297 }
1298
1299 // Constructing the simplified semi-affine sum of product/division/mod
1300 // expression from the flattened form in the desired sorted order of indices
1301 // of the various individual product/division/mod expressions.
1302 llvm::sort(indices);
1303 for (const std::pair<unsigned, unsigned> index : indices) {
1304 assert(indexToExprMap.lookup(index) &&
1305 "cannot find key in `indexToExprMap` map");
1306 expr = expr + indexToExprMap.lookup(index) * coefficients.lookup(index);
1307 }
1308
1309 // Local identifiers.
1310 for (unsigned j = numDims + numSymbols, e = flatExprs.size() - 1; j < e;
1311 j++) {
1312 // If the coefficient of the local expression is 0, continue as we need not
1313 // add it in out final expression.
1314 if (flatExprs[j] == 0 || addedToMap[j - numDims - numSymbols])
1315 continue;
1316 auto term = localExprs[j - numDims - numSymbols] * flatExprs[j];
1317 expr = expr + term;
1318 }
1319
1320 // Constant term.
1321 int64_t constTerm = flatExprs.back();
1322 if (constTerm != 0)
1323 expr = expr + constTerm;
1324 return expr;
1325}
1326
1332
1333// In pure affine t = expr * c, we multiply each coefficient of lhs with c.
1334//
1335// In case of semi affine multiplication expressions, t = expr * symbolic_expr,
1336// introduce a local variable p (= expr * symbolic_expr), and the affine
1337// expression expr * symbolic_expr is added to `localExprs`.
1339 assert(operandExprStack.size() >= 2);
1341 operandExprStack.pop_back();
1343
1344 // Flatten semi-affine multiplication expressions by introducing a local
1345 // variable in place of the product; the affine expression
1346 // corresponding to the quantifier is added to `localExprs`.
1347 if (!isa<AffineConstantExpr>(expr.getRHS())) {
1349 MLIRContext *context = expr.getContext();
1351 localExprs, context);
1353 localExprs, context);
1354 return addLocalVariableSemiAffine(mulLhs, rhs, a * b, lhs, lhs.size());
1355 }
1356
1357 // Get the RHS constant.
1358 int64_t rhsConst = rhs[getConstantIndex()];
1359 for (int64_t &lhsElt : lhs)
1360 lhsElt *= rhsConst;
1361
1362 return success();
1363}
1364
1366 assert(operandExprStack.size() >= 2);
1367 const auto &rhs = operandExprStack.back();
1368 auto &lhs = operandExprStack[operandExprStack.size() - 2];
1369 assert(lhs.size() == rhs.size());
1370 // Update the LHS in place.
1371 for (unsigned i = 0, e = rhs.size(); i < e; i++) {
1372 lhs[i] += rhs[i];
1373 }
1374 // Pop off the RHS.
1375 operandExprStack.pop_back();
1376 return success();
1377}
1378
1379//
1380// t = expr mod c <=> t = expr - c*q and c*q <= expr <= c*q + c - 1
1381//
1382// A mod expression "expr mod c" is thus flattened by introducing a new local
1383// variable q (= expr floordiv c), such that expr mod c is replaced with
1384// 'expr - c * q' and c * q <= expr <= c * q + c - 1 are added to localVarCst.
1385//
1386// In case of semi-affine modulo expressions, t = expr mod symbolic_expr,
1387// introduce a local variable m (= expr mod symbolic_expr), and the affine
1388// expression expr mod symbolic_expr is added to `localExprs`.
1390 assert(operandExprStack.size() >= 2);
1391
1393 operandExprStack.pop_back();
1395 MLIRContext *context = expr.getContext();
1396
1397 // Flatten semi affine modulo expressions by introducing a local
1398 // variable in place of the modulo value, and the affine expression
1399 // corresponding to the quantifier is added to `localExprs`.
1400 if (!isa<AffineConstantExpr>(expr.getRHS())) {
1403 lhs, numDims, numSymbols, localExprs, context);
1405 localExprs, context);
1406 AffineExpr modExpr = dividendExpr % divisorExpr;
1407 return addLocalVariableSemiAffine(modLhs, rhs, modExpr, lhs, lhs.size());
1408 }
1409
1410 int64_t rhsConst = rhs[getConstantIndex()];
1411 if (rhsConst <= 0)
1412 return failure();
1413
1414 // Check if the LHS expression is a multiple of modulo factor.
1415 unsigned i, e;
1416 for (i = 0, e = lhs.size(); i < e; i++)
1417 if (lhs[i] % rhsConst != 0)
1418 break;
1419 // If yes, modulo expression here simplifies to zero.
1420 if (i == lhs.size()) {
1421 llvm::fill(lhs, 0);
1422 return success();
1423 }
1424
1425 // Add a local variable for the quotient, i.e., expr % c is replaced by
1426 // (expr - q * c) where q = expr floordiv c. Do this while canceling out
1427 // the GCD of expr and c.
1428 SmallVector<int64_t, 8> floorDividend(lhs);
1429 uint64_t gcd = rhsConst;
1430 for (int64_t lhsElt : lhs)
1431 gcd = std::gcd(gcd, (uint64_t)std::abs(lhsElt));
1432 // Simplify the numerator and the denominator.
1433 if (gcd != 1) {
1434 for (int64_t &floorDividendElt : floorDividend)
1435 floorDividendElt = floorDividendElt / static_cast<int64_t>(gcd);
1436 }
1437 int64_t floorDivisor = rhsConst / static_cast<int64_t>(gcd);
1438
1439 // Construct the AffineExpr form of the floordiv to store in localExprs.
1440
1442 floorDividend, numDims, numSymbols, localExprs, context);
1443 AffineExpr divisorExpr = getAffineConstantExpr(floorDivisor, context);
1444 AffineExpr floorDivExpr = dividendExpr.floorDiv(divisorExpr);
1445 int loc;
1446 if ((loc = findLocalId(floorDivExpr)) == -1) {
1447 addLocalFloorDivId(floorDividend, floorDivisor, floorDivExpr);
1448 // Set result at top of stack to "lhs - rhsConst * q".
1449 lhs[getLocalVarStartIndex() + numLocals - 1] = -rhsConst;
1450 } else {
1451 // Reuse the existing local id.
1452 lhs[getLocalVarStartIndex() + loc] -= rhsConst;
1453 }
1454 return success();
1455}
1456
1457LogicalResult
1459 return visitDivExpr(expr, /*isCeil=*/true);
1460}
1461LogicalResult
1463 return visitDivExpr(expr, /*isCeil=*/false);
1464}
1465
1467 operandExprStack.emplace_back(SmallVector<int64_t, 32>(getNumCols(), 0));
1468 auto &eq = operandExprStack.back();
1469 assert(expr.getPosition() < numDims && "Inconsistent number of dims");
1470 eq[getDimStartIndex() + expr.getPosition()] = 1;
1471 return success();
1472}
1473
1474LogicalResult
1476 operandExprStack.emplace_back(SmallVector<int64_t, 32>(getNumCols(), 0));
1477 auto &eq = operandExprStack.back();
1478 assert(expr.getPosition() < numSymbols && "inconsistent number of symbols");
1479 eq[getSymbolStartIndex() + expr.getPosition()] = 1;
1480 return success();
1481}
1482
1483LogicalResult
1485 operandExprStack.emplace_back(SmallVector<int64_t, 32>(getNumCols(), 0));
1486 auto &eq = operandExprStack.back();
1487 eq[getConstantIndex()] = expr.getValue();
1488 return success();
1489}
1490
1491LogicalResult SimpleAffineExprFlattener::addLocalVariableSemiAffine(
1493 SmallVectorImpl<int64_t> &result, unsigned long resultSize) {
1494 assert(result.size() == resultSize &&
1495 "`result` vector passed is not of correct size");
1496 int loc;
1497 if ((loc = findLocalId(localExpr)) == -1) {
1498 if (failed(addLocalIdSemiAffine(lhs, rhs, localExpr)))
1499 return failure();
1500 }
1501 llvm::fill(result, 0);
1502 if (loc == -1)
1503 result[getLocalVarStartIndex() + numLocals - 1] = 1;
1504 else
1505 result[getLocalVarStartIndex() + loc] = 1;
1506 return success();
1507}
1508
1509// t = expr floordiv c <=> t = q, c * q <= expr <= c * q + c - 1
1510// A floordiv is thus flattened by introducing a new local variable q, and
1511// replacing that expression with 'q' while adding the constraints
1512// c * q <= expr <= c * q + c - 1 to localVarCst (done by
1513// IntegerRelation::addLocalFloorDiv).
1514//
1515// A ceildiv is similarly flattened:
1516// t = expr ceildiv c <=> t = (expr + c - 1) floordiv c
1517//
1518// In case of semi affine division expressions, t = expr floordiv symbolic_expr
1519// or t = expr ceildiv symbolic_expr, introduce a local variable q (= expr
1520// floordiv/ceildiv symbolic_expr), and the affine floordiv/ceildiv is added to
1521// `localExprs`.
1522LogicalResult SimpleAffineExprFlattener::visitDivExpr(AffineBinaryOpExpr expr,
1523 bool isCeil) {
1524 assert(operandExprStack.size() >= 2);
1525
1526 MLIRContext *context = expr.getContext();
1527 SmallVector<int64_t, 8> rhs = operandExprStack.back();
1528 operandExprStack.pop_back();
1529 SmallVector<int64_t, 8> &lhs = operandExprStack.back();
1530
1531 // Flatten semi affine division expressions by introducing a local
1532 // variable in place of the quotient, and the affine expression corresponding
1533 // to the quantifier is added to `localExprs`.
1534 if (!isa<AffineConstantExpr>(expr.getRHS())) {
1535 SmallVector<int64_t, 8> divLhs(lhs);
1537 localExprs, context);
1539 localExprs, context);
1540 AffineExpr divExpr = isCeil ? a.ceilDiv(b) : a.floorDiv(b);
1541 return addLocalVariableSemiAffine(divLhs, rhs, divExpr, lhs, lhs.size());
1542 }
1543
1544 // This is a pure affine expr; the RHS is a positive constant.
1545 int64_t rhsConst = rhs[getConstantIndex()];
1546 if (rhsConst <= 0)
1547 return failure();
1548
1549 // Simplify the floordiv, ceildiv if possible by canceling out the greatest
1550 // common divisors of the numerator and denominator.
1551 uint64_t gcd = std::abs(rhsConst);
1552 for (int64_t lhsElt : lhs)
1553 gcd = std::gcd(gcd, (uint64_t)std::abs(lhsElt));
1554 // Simplify the numerator and the denominator.
1555 if (gcd != 1) {
1556 for (int64_t &lhsElt : lhs)
1557 lhsElt = lhsElt / static_cast<int64_t>(gcd);
1558 }
1559 int64_t divisor = rhsConst / static_cast<int64_t>(gcd);
1560 // If the divisor becomes 1, the updated LHS is the result. (The
1561 // divisor can't be negative since rhsConst is positive).
1562 if (divisor == 1)
1563 return success();
1564
1565 // If the divisor cannot be simplified to one, we will have to retain
1566 // the ceil/floor expr (simplified up until here). Add an existential
1567 // quantifier to express its result, i.e., expr1 div expr2 is replaced
1568 // by a new identifier, q.
1569 AffineExpr a =
1571 AffineExpr b = getAffineConstantExpr(divisor, context);
1572
1573 int loc;
1574 AffineExpr divExpr = isCeil ? a.ceilDiv(b) : a.floorDiv(b);
1575 if ((loc = findLocalId(divExpr)) == -1) {
1576 if (!isCeil) {
1577 SmallVector<int64_t, 8> dividend(lhs);
1578 addLocalFloorDivId(dividend, divisor, divExpr);
1579 } else {
1580 // lhs ceildiv c <=> (lhs + c - 1) floordiv c
1581 SmallVector<int64_t, 8> dividend(lhs);
1582 dividend.back() += divisor - 1;
1583 addLocalFloorDivId(dividend, divisor, divExpr);
1584 }
1585 }
1586 // Set the expression on stack to the local var introduced to capture the
1587 // result of the division (floor or ceil).
1588 llvm::fill(lhs, 0);
1589 if (loc == -1)
1590 lhs[getLocalVarStartIndex() + numLocals - 1] = 1;
1591 else
1592 lhs[getLocalVarStartIndex() + loc] = 1;
1593 return success();
1594}
1595
1596// Add a local identifier (needed to flatten a mod, floordiv, ceildiv expr).
1597// The local identifier added is always a floordiv of a pure add/mul affine
1598// function of other identifiers, coefficients of which are specified in
1599// dividend and with respect to a positive constant divisor. localExpr is the
1600// simplified tree expression (AffineExpr) corresponding to the quantifier.
1602 int64_t divisor,
1603 AffineExpr localExpr) {
1604 assert(divisor > 0 && "positive constant divisor expected");
1606 subExpr.insert(subExpr.begin() + getLocalVarStartIndex() + numLocals, 0);
1607 localExprs.push_back(localExpr);
1608 numLocals++;
1609 // dividend and divisor are not used here; an override of this method uses it.
1610}
1611
1615 subExpr.insert(subExpr.begin() + getLocalVarStartIndex() + numLocals, 0);
1616 localExprs.push_back(localExpr);
1617 ++numLocals;
1618 // lhs and rhs are not used here; an override of this method uses them.
1619 return success();
1620}
1621
1622int SimpleAffineExprFlattener::findLocalId(AffineExpr localExpr) {
1624 if ((it = llvm::find(localExprs, localExpr)) == localExprs.end())
1625 return -1;
1626 return it - localExprs.begin();
1627}
1628
1629/// Simplify the affine expression by flattening it and reconstructing it.
1631 unsigned numSymbols) {
1632 // Simplify semi-affine expressions separately.
1633 if (!expr.isPureAffine())
1634 expr = simplifySemiAffine(expr, numDims, numSymbols);
1635
1636 SimpleAffineExprFlattener flattener(numDims, numSymbols);
1637 // has poison expression
1638 if (failed(flattener.walkPostOrder(expr)))
1639 return expr;
1640 ArrayRef<int64_t> flattenedExpr = flattener.operandExprStack.back();
1641 if (!expr.isPureAffine() &&
1642 expr == getAffineExprFromFlatForm(flattenedExpr, numDims, numSymbols,
1643 flattener.localExprs,
1644 expr.getContext()))
1645 return expr;
1646 AffineExpr simplifiedExpr =
1647 expr.isPureAffine()
1648 ? getAffineExprFromFlatForm(flattenedExpr, numDims, numSymbols,
1649 flattener.localExprs, expr.getContext())
1650 : getSemiAffineExprFromFlatForm(flattenedExpr, numDims, numSymbols,
1651 flattener.localExprs,
1652 expr.getContext());
1653
1654 flattener.operandExprStack.pop_back();
1655 assert(flattener.operandExprStack.empty());
1656 return simplifiedExpr;
1657}
1658
1659std::optional<int64_t> mlir::getBoundForAffineExpr(
1660 AffineExpr expr, unsigned numDims, unsigned numSymbols,
1661 ArrayRef<std::optional<int64_t>> constLowerBounds,
1662 ArrayRef<std::optional<int64_t>> constUpperBounds, bool isUpper) {
1663 // Handle divs and mods.
1664 if (auto binOpExpr = dyn_cast<AffineBinaryOpExpr>(expr)) {
1665 // If the LHS of a floor or ceil is bounded and the RHS is a constant, we
1666 // can compute an upper bound.
1667 if (binOpExpr.getKind() == AffineExprKind::FloorDiv) {
1668 auto rhsConst = dyn_cast<AffineConstantExpr>(binOpExpr.getRHS());
1669 if (!rhsConst || rhsConst.getValue() < 1)
1670 return std::nullopt;
1671 auto bound =
1672 getBoundForAffineExpr(binOpExpr.getLHS(), numDims, numSymbols,
1673 constLowerBounds, constUpperBounds, isUpper);
1674 if (!bound)
1675 return std::nullopt;
1676 return divideFloorSigned(*bound, rhsConst.getValue());
1677 }
1678 if (binOpExpr.getKind() == AffineExprKind::CeilDiv) {
1679 auto rhsConst = dyn_cast<AffineConstantExpr>(binOpExpr.getRHS());
1680 if (rhsConst && rhsConst.getValue() >= 1) {
1681 auto bound =
1682 getBoundForAffineExpr(binOpExpr.getLHS(), numDims, numSymbols,
1683 constLowerBounds, constUpperBounds, isUpper);
1684 if (!bound)
1685 return std::nullopt;
1686 return divideCeilSigned(*bound, rhsConst.getValue());
1687 }
1688 return std::nullopt;
1689 }
1690 if (binOpExpr.getKind() == AffineExprKind::Mod) {
1691 // lhs mod c is always <= c - 1 and non-negative. In addition, if `lhs` is
1692 // bounded such that lb <= lhs <= ub and lb floordiv c == ub floordiv c
1693 // (same "interval"), then lb mod c <= lhs mod c <= ub mod c.
1694 auto rhsConst = dyn_cast<AffineConstantExpr>(binOpExpr.getRHS());
1695 if (rhsConst && rhsConst.getValue() >= 1) {
1696 int64_t rhsConstVal = rhsConst.getValue();
1697 auto lb = getBoundForAffineExpr(binOpExpr.getLHS(), numDims, numSymbols,
1698 constLowerBounds, constUpperBounds,
1699 /*isUpper=*/false);
1700 auto ub =
1701 getBoundForAffineExpr(binOpExpr.getLHS(), numDims, numSymbols,
1702 constLowerBounds, constUpperBounds, isUpper);
1703 if (ub && lb &&
1704 divideFloorSigned(*lb, rhsConstVal) ==
1705 divideFloorSigned(*ub, rhsConstVal))
1706 return isUpper ? mod(*ub, rhsConstVal) : mod(*lb, rhsConstVal);
1707 return isUpper ? rhsConstVal - 1 : 0;
1708 }
1709 }
1710 }
1711 // Flatten the expression.
1712 SimpleAffineExprFlattener flattener(numDims, numSymbols);
1713 auto simpleResult = flattener.walkPostOrder(expr);
1714 // has poison expression
1715 if (failed(simpleResult))
1716 return std::nullopt;
1717 ArrayRef<int64_t> flattenedExpr = flattener.operandExprStack.back();
1718 // TODO: Handle local variables. We can get hold of flattener.localExprs and
1719 // get bound on the local expr recursively.
1720 if (flattener.numLocals > 0)
1721 return std::nullopt;
1722 int64_t bound = 0;
1723 // Substitute the constant lower or upper bound for the dimensional or
1724 // symbolic input depending on `isUpper` to determine the bound.
1725 for (unsigned i = 0, e = numDims + numSymbols; i < e; ++i) {
1726 if (flattenedExpr[i] > 0) {
1727 auto &constBound = isUpper ? constUpperBounds[i] : constLowerBounds[i];
1728 if (!constBound)
1729 return std::nullopt;
1730 bound += *constBound * flattenedExpr[i];
1731 } else if (flattenedExpr[i] < 0) {
1732 auto &constBound = isUpper ? constLowerBounds[i] : constUpperBounds[i];
1733 if (!constBound)
1734 return std::nullopt;
1735 bound += *constBound * flattenedExpr[i];
1736 }
1737 }
1738 // Constant term.
1739 bound += flattenedExpr.back();
1740 return bound;
1741}
return success()
static int64_t product(ArrayRef< int64_t > vals)
lhs
static AffineExpr simplifyMul(AffineExpr lhs, AffineExpr rhs)
Simplify a multiply expression. Return nullptr if it can't be simplified.
static AffineExpr simplifyMod(AffineExpr lhs, AffineExpr rhs)
static AffineExpr getSemiAffineExprFromFlatForm(ArrayRef< int64_t > flatExprs, unsigned numDims, unsigned numSymbols, ArrayRef< AffineExpr > localExprs, MLIRContext *context)
Constructs a semi-affine expression from a flat ArrayRef. If there are local identifiers (neither dim...
static AffineExpr simplifyCeilDiv(AffineExpr lhs, AffineExpr rhs)
static AffineExpr simplifyFloorDiv(AffineExpr lhs, AffineExpr rhs)
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`
Affine binary operation expression.
Definition AffineExpr.h:214
AffineExpr getLHS() const
AffineBinaryOpExpr(AffineExpr::ImplType *ptr)
detail::AffineBinaryOpExprStorage ImplType
Definition AffineExpr.h:216
AffineExpr getRHS() const
An integer constant appearing in affine expression.
Definition AffineExpr.h:239
AffineConstantExpr(AffineExpr::ImplType *ptr=nullptr)
detail::AffineConstantExprStorage ImplType
Definition AffineExpr.h:241
int64_t getValue() const
A dimensional identifier appearing in an affine expression.
Definition AffineExpr.h:223
AffineDimExpr(AffineExpr::ImplType *ptr)
detail::AffineDimExprStorage ImplType
Definition AffineExpr.h:225
unsigned getPosition() const
See documentation for AffineExprVisitorBase.
RetTy walkPostOrder(AffineExpr expr)
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.
AffineExpr shiftDims(unsigned numDims, unsigned shift, unsigned offset=0) const
Replace dims[offset ... numDims) by dims[offset + shift ... shift + numDims).
bool isSymbolicOrConstant() const
Returns true if this expression is made out of only symbols and constants, i.e., it does not involve ...
AffineExpr operator+(int64_t v) const
AffineExpr operator*(int64_t v) const
constexpr AffineExpr()
Definition AffineExpr.h:72
bool operator==(AffineExpr other) const
Definition AffineExpr.h:76
bool isPureAffine() const
Returns true if this is a pure affine expression, i.e., multiplication, floordiv, ceildiv,...
AffineExpr shiftSymbols(unsigned numSymbols, unsigned shift, unsigned offset=0) const
Replace symbols[offset ... numSymbols) by symbols[offset + shift ... shift + numSymbols).
AffineExpr operator-() const
AffineExpr floorDiv(uint64_t v) const
ImplType * expr
Definition AffineExpr.h:196
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.
bool isMultipleOf(int64_t factor) const
Return true if the affine expression is a multiple of 'factor'.
int64_t getLargestKnownDivisor() const
Returns the greatest known integral divisor of this affine expression.
AffineExpr compose(AffineMap map) const
Compose with an AffineMap.
bool isFunctionOfDim(unsigned position) const
Return true if the affine expression involves AffineDimExpr position.
bool isFunctionOfSymbol(unsigned position) const
Return true if the affine expression involves AffineSymbolExpr position.
AffineExpr replaceDims(ArrayRef< AffineExpr > dimReplacements) const
Dim-only version of replaceDimsAndSymbols.
AffineExpr operator%(uint64_t v) const
MLIRContext * getContext() const
AffineExpr replace(AffineExpr expr, AffineExpr replacement) const
Sparse replace method.
AffineExpr replaceSymbols(ArrayRef< AffineExpr > symReplacements) const
Symbol-only version of replaceDimsAndSymbols.
detail::AffineExprStorage ImplType
Definition AffineExpr.h:70
AffineExpr ceilDiv(uint64_t v) const
void print(raw_ostream &os) const
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
Definition AffineMap.h:46
ArrayRef< AffineExpr > getResults() const
A symbolic identifier appearing in an affine expression.
Definition AffineExpr.h:231
unsigned getPosition() const
detail::AffineDimExprStorage ImplType
Definition AffineExpr.h:233
AffineSymbolExpr(AffineExpr::ImplType *ptr)
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
StorageUniquer & getAffineUniquer()
Returns the storage uniquer used for creating affine constructs.
virtual void addLocalFloorDivId(ArrayRef< int64_t > dividend, int64_t divisor, AffineExpr localExpr)
LogicalResult visitSymbolExpr(AffineSymbolExpr expr)
std::vector< SmallVector< int64_t, 8 > > operandExprStack
LogicalResult visitDimExpr(AffineDimExpr expr)
LogicalResult visitFloorDivExpr(AffineBinaryOpExpr expr)
LogicalResult visitConstantExpr(AffineConstantExpr expr)
virtual LogicalResult addLocalIdSemiAffine(ArrayRef< int64_t > lhs, ArrayRef< int64_t > rhs, AffineExpr localExpr)
Add a local identifier (needed to flatten a mod, floordiv, ceildiv, mul expr) when the rhs is a symbo...
LogicalResult visitModExpr(AffineBinaryOpExpr expr)
LogicalResult visitAddExpr(AffineBinaryOpExpr expr)
LogicalResult visitCeilDivExpr(AffineBinaryOpExpr expr)
LogicalResult visitMulExpr(AffineBinaryOpExpr expr)
SmallVector< AffineExpr, 4 > localExprs
SimpleAffineExprFlattener(unsigned numDims, unsigned numSymbols)
A utility class to get or create instances of "storage classes".
Storage * get(function_ref< void(Storage *)> initFn, TypeID id, Args &&...args)
Gets a uniqued instance of 'Storage'.
A utility result that is used to signal how to proceed with an ongoing walk:
Definition WalkResult.h:29
AttrTypeReplacer.
Include the generated interface declarations.
raw_ostream & operator<<(raw_ostream &os, const AliasResult &result)
std::optional< int64_t > getBoundForAffineExpr(AffineExpr expr, unsigned numDims, unsigned numSymbols, ArrayRef< std::optional< int64_t > > constLowerBounds, ArrayRef< std::optional< int64_t > > constUpperBounds, bool isUpper)
Get a lower or upper (depending on isUpper) bound for expr while using the constant lower and upper b...
AffineExprKind
Definition AffineExpr.h:40
@ CeilDiv
RHS of ceildiv is always a constant or a symbolic expression.
Definition AffineExpr.h:50
@ Mul
RHS of mul is always a constant or a symbolic expression.
Definition AffineExpr.h:43
@ Mod
RHS of mod is always a constant or a symbolic expression with a positive value.
Definition AffineExpr.h:46
@ DimId
Dimensional identifier.
Definition AffineExpr.h:59
@ FloorDiv
RHS of floordiv is always a constant or a symbolic expression.
Definition AffineExpr.h:48
@ Constant
Constant integer.
Definition AffineExpr.h:57
@ SymbolId
Symbolic identifier.
Definition AffineExpr.h:61
AffineExpr getAffineBinaryOpExpr(AffineExprKind kind, AffineExpr lhs, AffineExpr rhs)
AffineExpr getAffineExprFromFlatForm(ArrayRef< int64_t > flatExprs, unsigned numDims, unsigned numSymbols, ArrayRef< AffineExpr > localExprs, MLIRContext *context)
Constructs an affine expression from a flat ArrayRef.
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:120
AffineExpr simplifyAffineExpr(AffineExpr expr, unsigned numDims, unsigned numSymbols)
Simplify an affine expression by flattening and some amount of simple analysis.
SmallVector< AffineExpr > getAffineConstantExprs(ArrayRef< int64_t > constants, MLIRContext *context)
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147
AffineExpr getAffineSymbolExpr(unsigned position, MLIRContext *context)
A binary operation appearing in an affine expression.
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.