blob: 2b6d68020923d6782dff1e944ea9b88d37287159 [file] [log] [blame]
MLIR Teamf85a6262018-06-27 11:03:08 -07001//===- AffineMap.cpp - MLIR Affine Map Classes ----------------------------===//
2//
3// Copyright 2019 The MLIR Authors.
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16// =============================================================================
17
18#include "mlir/IR/AffineMap.h"
Uday Bondhugula3934d4d2018-07-09 09:00:25 -070019#include "mlir/IR/AffineExpr.h"
MLIR Teamf85a6262018-06-27 11:03:08 -070020#include "llvm/ADT/StringRef.h"
Uday Bondhugulae082aad2018-07-11 21:19:31 -070021#include "llvm/Support/MathExtras.h"
MLIR Teamf85a6262018-06-27 11:03:08 -070022
23using namespace mlir;
24
Uday Bondhugula015cbb12018-07-03 20:16:08 -070025AffineMap::AffineMap(unsigned numDims, unsigned numSymbols, unsigned numResults,
Uday Bondhugula0115dbb2018-07-11 21:31:07 -070026 AffineExpr *const *results, AffineExpr *const *rangeSizes)
Uday Bondhugula015cbb12018-07-03 20:16:08 -070027 : numDims(numDims), numSymbols(numSymbols), numResults(numResults),
Uday Bondhugula0115dbb2018-07-11 21:31:07 -070028 results(results), rangeSizes(rangeSizes) {}
Uday Bondhugula3934d4d2018-07-09 09:00:25 -070029
MLIR Teamcfeca5f2018-08-14 12:43:51 -070030bool AffineMap::isIdentity() const {
31 if (getNumDims() != getNumResults())
32 return false;
33 ArrayRef<AffineExpr *> results = getResults();
MLIR Team04914bc2018-08-15 15:14:45 -070034 for (unsigned i = 0, numDims = getNumDims(); i < numDims; ++i) {
35 auto *expr = dyn_cast<AffineDimExpr>(results[i]);
36 if (!expr || expr->getPosition() != i)
MLIR Teamcfeca5f2018-08-14 12:43:51 -070037 return false;
38 }
39 return true;
40}
41
Tatiana Shpeismande8829f2018-08-24 23:38:14 -070042bool AffineMap::isSingleConstant() const {
43 return getNumResults() == 1 && isa<AffineConstantExpr>(getResult(0));
44}
45
46int64_t AffineMap::getSingleConstantValue() const {
47 assert(isSingleConstant() && "map must have a single constant result");
48 return dyn_cast<AffineConstantExpr>(getResult(0))->getValue();
49}
50
Uday Bondhugula970f5b82018-08-01 22:02:00 -070051/// Simplify add expression. Return nullptr if it can't be simplified.
Uday Bondhugulae082aad2018-07-11 21:19:31 -070052AffineExpr *AffineBinaryOpExpr::simplifyAdd(AffineExpr *lhs, AffineExpr *rhs,
53 MLIRContext *context) {
Uday Bondhugula970f5b82018-08-01 22:02:00 -070054 auto *lhsConst = dyn_cast<AffineConstantExpr>(lhs);
55 auto *rhsConst = dyn_cast<AffineConstantExpr>(rhs);
Uday Bondhugulae082aad2018-07-11 21:19:31 -070056
Uday Bondhugula970f5b82018-08-01 22:02:00 -070057 // Fold if both LHS, RHS are a constant.
58 if (lhsConst && rhsConst)
59 return AffineConstantExpr::get(lhsConst->getValue() + rhsConst->getValue(),
60 context);
61
62 // Canonicalize so that only the RHS is a constant. (4 + d0 becomes d0 + 4).
63 // If only one of them is a symbolic expressions, make it the RHS.
Uday Bondhugulacbe4cca2018-07-19 13:07:16 -070064 if (isa<AffineConstantExpr>(lhs) ||
Uday Bondhugula970f5b82018-08-01 22:02:00 -070065 (lhs->isSymbolicOrConstant() && !rhs->isSymbolicOrConstant())) {
Uday Bondhugulac1faf662018-07-19 14:08:50 -070066 return AffineBinaryOpExpr::get(Kind::Add, rhs, lhs, context);
Uday Bondhugula970f5b82018-08-01 22:02:00 -070067 }
68
69 // At this point, if there was a constant, it would be on the right.
70
71 // Addition with a zero is a noop, return the other input.
72 if (rhsConst) {
73 if (rhsConst->getValue() == 0)
74 return lhs;
75 }
76 // Fold successive additions like (d0 + 2) + 3 into d0 + 5.
77 auto *lBin = dyn_cast<AffineBinaryOpExpr>(lhs);
78 if (lBin && rhsConst && lBin->getKind() == Kind::Add) {
79 if (auto *lrhs = dyn_cast<AffineConstantExpr>(lBin->getRHS()))
80 return AffineBinaryOpExpr::get(
81 Kind::Add, lBin->getLHS(),
82 AffineConstantExpr::get(lrhs->getValue() + rhsConst->getValue(),
83 context),
84 context);
85 }
86
87 // When doing successive additions, bring constant to the right: turn (d0 + 2)
88 // + d1 into (d0 + d1) + 2.
89 if (lBin && lBin->getKind() == Kind::Add) {
90 if (auto *lrhs = dyn_cast<AffineConstantExpr>(lBin->getRHS())) {
91 return AffineBinaryOpExpr::get(
92 Kind::Add,
93 AffineBinaryOpExpr::get(Kind::Add, lBin->getLHS(), rhs, context),
94 lrhs, context);
95 }
96 }
Uday Bondhugulae082aad2018-07-11 21:19:31 -070097
Uday Bondhugula3934d4d2018-07-09 09:00:25 -070098 return nullptr;
Uday Bondhugula3934d4d2018-07-09 09:00:25 -070099}
100
Uday Bondhugula970f5b82018-08-01 22:02:00 -0700101/// Simplify a multiply expression. Return nullptr if it can't be simplified.
Uday Bondhugulae082aad2018-07-11 21:19:31 -0700102AffineExpr *AffineBinaryOpExpr::simplifyMul(AffineExpr *lhs, AffineExpr *rhs,
103 MLIRContext *context) {
Uday Bondhugula970f5b82018-08-01 22:02:00 -0700104 auto *lhsConst = dyn_cast<AffineConstantExpr>(lhs);
105 auto *rhsConst = dyn_cast<AffineConstantExpr>(rhs);
106
107 if (lhsConst && rhsConst)
108 return AffineConstantExpr::get(lhsConst->getValue() * rhsConst->getValue(),
109 context);
Uday Bondhugulae082aad2018-07-11 21:19:31 -0700110
Uday Bondhugulacbe4cca2018-07-19 13:07:16 -0700111 assert(lhs->isSymbolicOrConstant() || rhs->isSymbolicOrConstant());
Uday Bondhugulae082aad2018-07-11 21:19:31 -0700112
113 // Canonicalize the mul expression so that the constant/symbolic term is the
114 // RHS. If both the lhs and rhs are symbolic, swap them if the lhs is a
115 // constant. (Note that a constant is trivially symbolic).
Uday Bondhugulacbe4cca2018-07-19 13:07:16 -0700116 if (!rhs->isSymbolicOrConstant() || isa<AffineConstantExpr>(lhs)) {
Uday Bondhugulae082aad2018-07-11 21:19:31 -0700117 // At least one of them has to be symbolic.
Uday Bondhugulac1faf662018-07-19 14:08:50 -0700118 return AffineBinaryOpExpr::get(Kind::Mul, rhs, lhs, context);
Uday Bondhugulae082aad2018-07-11 21:19:31 -0700119 }
120
Uday Bondhugula970f5b82018-08-01 22:02:00 -0700121 // At this point, if there was a constant, it would be on the right.
122
123 // Multiplication with a one is a noop, return the other input.
124 if (rhsConst) {
125 if (rhsConst->getValue() == 1)
126 return lhs;
127 // Multiplication with zero.
128 if (rhsConst->getValue() == 0)
129 return rhsConst;
130 }
131
132 // Fold successive multiplications: eg: (d0 * 2) * 3 into d0 * 6.
133 auto *lBin = dyn_cast<AffineBinaryOpExpr>(lhs);
134 if (lBin && rhsConst && lBin->getKind() == Kind::Mul) {
135 if (auto *lrhs = dyn_cast<AffineConstantExpr>(lBin->getRHS()))
136 return AffineBinaryOpExpr::get(
137 Kind::Mul, lBin->getLHS(),
138 AffineConstantExpr::get(lrhs->getValue() * rhsConst->getValue(),
139 context),
140 context);
141 }
142
143 // When doing successive multiplication, bring constant to the right: turn (d0
144 // * 2) * d1 into (d0 * d1) * 2.
145 if (lBin && lBin->getKind() == Kind::Mul) {
146 if (auto *lrhs = dyn_cast<AffineConstantExpr>(lBin->getRHS())) {
147 return AffineBinaryOpExpr::get(
148 Kind::Mul,
149 AffineBinaryOpExpr::get(Kind::Mul, lBin->getLHS(), rhs, context),
150 lrhs, context);
151 }
152 }
153
Uday Bondhugulae082aad2018-07-11 21:19:31 -0700154 return nullptr;
Uday Bondhugulae082aad2018-07-11 21:19:31 -0700155}
156
157AffineExpr *AffineBinaryOpExpr::simplifyFloorDiv(AffineExpr *lhs,
158 AffineExpr *rhs,
159 MLIRContext *context) {
Uday Bondhugula970f5b82018-08-01 22:02:00 -0700160 auto *lhsConst = dyn_cast<AffineConstantExpr>(lhs);
161 auto *rhsConst = dyn_cast<AffineConstantExpr>(rhs);
162
163 if (lhsConst && rhsConst)
164 return AffineConstantExpr::get(lhsConst->getValue() / rhsConst->getValue(),
165 context);
166
167 // Fold floordiv of a multiply with a constant that is a multiple of the
168 // divisor. Eg: (i * 128) floordiv 64 = i * 2.
169 if (rhsConst) {
170 auto *lBin = dyn_cast<AffineBinaryOpExpr>(lhs);
171 if (lBin && lBin->getKind() == Kind::Mul) {
172 if (auto *lrhs = dyn_cast<AffineConstantExpr>(lBin->getRHS())) {
173 // rhsConst is known to be positive if a constant.
174 if (lrhs->getValue() % rhsConst->getValue() == 0)
175 return AffineBinaryOpExpr::get(
176 Kind::Mul, lBin->getLHS(),
177 AffineConstantExpr::get(lrhs->getValue() / rhsConst->getValue(),
178 context),
179 context);
180 }
181 }
182 }
Uday Bondhugulae082aad2018-07-11 21:19:31 -0700183
184 return nullptr;
Uday Bondhugulae082aad2018-07-11 21:19:31 -0700185}
186
187AffineExpr *AffineBinaryOpExpr::simplifyCeilDiv(AffineExpr *lhs,
188 AffineExpr *rhs,
189 MLIRContext *context) {
Uday Bondhugula970f5b82018-08-01 22:02:00 -0700190 auto *lhsConst = dyn_cast<AffineConstantExpr>(lhs);
191 auto *rhsConst = dyn_cast<AffineConstantExpr>(rhs);
192
193 if (lhsConst && rhsConst)
194 return AffineConstantExpr::get(
195 (int64_t)llvm::divideCeil((uint64_t)lhsConst->getValue(),
196 (uint64_t)rhsConst->getValue()),
197 context);
198
199 // Fold ceildiv of a multiply with a constant that is a multiple of the
200 // divisor. Eg: (i * 128) ceildiv 64 = i * 2.
201 if (rhsConst) {
202 auto *lBin = dyn_cast<AffineBinaryOpExpr>(lhs);
203 if (lBin && lBin->getKind() == Kind::Mul) {
204 if (auto *lrhs = dyn_cast<AffineConstantExpr>(lBin->getRHS())) {
205 // rhsConst is known to be positive if a constant.
206 if (lrhs->getValue() % rhsConst->getValue() == 0)
207 return AffineBinaryOpExpr::get(
208 Kind::Mul, lBin->getLHS(),
209 AffineConstantExpr::get(lrhs->getValue() / rhsConst->getValue(),
210 context),
211 context);
212 }
213 }
214 }
Uday Bondhugulae082aad2018-07-11 21:19:31 -0700215
216 return nullptr;
Uday Bondhugulae082aad2018-07-11 21:19:31 -0700217}
218
219AffineExpr *AffineBinaryOpExpr::simplifyMod(AffineExpr *lhs, AffineExpr *rhs,
220 MLIRContext *context) {
Uday Bondhugula257339b2018-08-21 10:32:24 -0700221 auto *lhsConst = dyn_cast<AffineConstantExpr>(lhs);
222 auto *rhsConst = dyn_cast<AffineConstantExpr>(rhs);
223
224 if (lhsConst && rhsConst)
225 return AffineConstantExpr::get(lhsConst->getValue() % rhsConst->getValue(),
226 context);
227
228 // Fold modulo of a multiply with a constant that is a multiple of the
229 // modulo factor to zero. Eg: (i * 128) mod 64 = 0.
230 if (rhsConst) {
231 auto *lBin = dyn_cast<AffineBinaryOpExpr>(lhs);
232 if (lBin && lBin->getKind() == Kind::Mul) {
233 if (auto *lrhs = dyn_cast<AffineConstantExpr>(lBin->getRHS())) {
234 // rhsConst is known to be positive if a constant.
235 if (lrhs->getValue() % rhsConst->getValue() == 0)
236 return AffineConstantExpr::get(0, context);
237 }
238 }
239 }
Uday Bondhugulae082aad2018-07-11 21:19:31 -0700240
241 return nullptr;
Uday Bondhugula257339b2018-08-21 10:32:24 -0700242 // TODO(bondhugula): In general, this can be simplified more by using the GCD
243 // test, or in general using quantifier elimination (add two new variables q
244 // and r, and eliminate all variables from the linear system other than r. All
245 // of this can be done through mlir/Analysis/'s FlatAffineConstraints.
Uday Bondhugulae082aad2018-07-11 21:19:31 -0700246}