11
11
#include " mlir/Dialect/MemRef/IR/MemRef.h"
12
12
#include " mlir/Dialect/UB/IR/UBOps.h"
13
13
#include " mlir/Dialect/Utils/StaticValueUtils.h"
14
+ #include " mlir/IR/AffineExpr.h"
14
15
#include " mlir/IR/AffineExprVisitor.h"
15
16
#include " mlir/IR/IRMapping.h"
16
17
#include " mlir/IR/IntegerSet.h"
17
18
#include " mlir/IR/Matchers.h"
18
19
#include " mlir/IR/OpDefinition.h"
19
20
#include " mlir/IR/PatternMatch.h"
21
+ #include " mlir/IR/Value.h"
20
22
#include " mlir/Interfaces/ShapedOpInterfaces.h"
21
23
#include " mlir/Interfaces/ValueBoundsOpInterface.h"
22
24
#include " mlir/Transforms/InliningUtils.h"
26
28
#include " llvm/ADT/SmallVectorExtras.h"
27
29
#include " llvm/ADT/TypeSwitch.h"
28
30
#include " llvm/Support/Debug.h"
31
+ #include " llvm/Support/LogicalResult.h"
29
32
#include " llvm/Support/MathExtras.h"
33
+ #include < limits>
30
34
#include < numeric>
31
35
#include < optional>
32
36
@@ -1042,6 +1046,59 @@ simplifyMapWithOperands(AffineMap &map, ArrayRef<Value> operands) {
1042
1046
map.getContext ());
1043
1047
}
1044
1048
1049
+ // / Assuming `dimOrSym` is a quantity in `map` that is defined by `minOp`,
1050
+ // / replaces the patterns:
1051
+ // / ```
1052
+ // / dimOrSym.ceildiv(cst) * cst
1053
+ // / (dimOrSym + cst - 1).floordiv(cst) * cst
1054
+ // / ```
1055
+ // / by `cst` in `map`.
1056
+ // / This simplification is valid iff `minOp` is guaranteed to be nonnegative.
1057
+ // / Additionally, allows the caller to pass `affineMinKnownToBeNonNegative` to
1058
+ // / inject static information that may not be statically discoverable.
1059
+ // / Warning: ValueBoundsConstraintSet::computeConstantBound is needed to check
1060
+ // / for the nonnegative case, if `affineMinKnownToBeNonNegative` is false.
1061
+ static LogicalResult replaceAffineMinBoundingBoxExpression (
1062
+ AffineMinOp minOp, AffineExpr dimOrSym, AffineMap *map,
1063
+ bool affineMinKnownToBeNonNegative = false ) {
1064
+ auto affineMinMap = minOp.getAffineMap ();
1065
+ if (!affineMinKnownToBeNonNegative) {
1066
+ ValueRange values = minOp->getOperands ();
1067
+ for (unsigned i = 0 , e = affineMinMap.getNumResults (); i < e; ++i) {
1068
+ AffineMap row = affineMinMap.getSubMap (ArrayRef<unsigned >{i});
1069
+ FailureOr<int64_t > lowerBound =
1070
+ ValueBoundsConstraintSet::computeConstantBound (
1071
+ presburger::BoundType::LB, {row, values},
1072
+ /* stopCondition=*/ nullptr ,
1073
+ /* closedUB=*/ true );
1074
+ if (failed (lowerBound) || lowerBound.value () < 0 )
1075
+ return failure ();
1076
+ }
1077
+ }
1078
+
1079
+ AffineMap initialMap = *map;
1080
+ for (unsigned i = 0 , e = affineMinMap.getNumResults (); i != e; ++i) {
1081
+ auto m = affineMinMap.getSubMap (ArrayRef<unsigned >{i});
1082
+ // TODO: this should also work with nonnegative symbolic divisors.
1083
+ if (!m.isSingleConstant ())
1084
+ continue ;
1085
+
1086
+ auto cst = m.getSingleConstantResult ();
1087
+ DenseMap<AffineExpr, AffineExpr> repl;
1088
+ // dimOrSym.ceilDiv(cst) * cst -> cst
1089
+ repl[dimOrSym.ceilDiv (cst) * cst] =
1090
+ getAffineConstantExpr (cst, minOp.getContext ());
1091
+ // (dimOrSym + cst - 1).floorDiv(cst) * cst -> cst
1092
+ repl[(dimOrSym + cst - 1 ).floorDiv (cst) * cst] =
1093
+ getAffineConstantExpr (cst, minOp.getContext ());
1094
+ auto newMap = map->replace (repl);
1095
+ if (newMap == *map)
1096
+ continue ;
1097
+ *map = newMap;
1098
+ }
1099
+ return success (*map != initialMap);
1100
+ }
1101
+
1045
1102
// / Replace all occurrences of AffineExpr at position `pos` in `map` by the
1046
1103
// / defining AffineApplyOp expression and operands.
1047
1104
// / When `dimOrSymbolPosition < dims.size()`, AffineDimExpr@[pos] is replaced.
@@ -1052,10 +1109,13 @@ simplifyMapWithOperands(AffineMap &map, ArrayRef<Value> operands) {
1052
1109
// / 2. `map` dim and symbols are gradually shifted to higher positions.
1053
1110
// / 3. Old `dim` and `sym` entries are replaced by nullptr
1054
1111
// / This avoids the need for any bookkeeping.
1112
+ // / If `replaceAffineMin` is set to true, additionally triggers more expensive
1113
+ // / replacements involving affine_min operations.
1055
1114
static LogicalResult replaceDimOrSym (AffineMap *map,
1056
1115
unsigned dimOrSymbolPosition,
1057
1116
SmallVectorImpl<Value> &dims,
1058
- SmallVectorImpl<Value> &syms) {
1117
+ SmallVectorImpl<Value> &syms,
1118
+ bool replaceAffineMin) {
1059
1119
MLIRContext *ctx = map->getContext ();
1060
1120
bool isDimReplacement = (dimOrSymbolPosition < dims.size ());
1061
1121
unsigned pos = isDimReplacement ? dimOrSymbolPosition
@@ -1064,6 +1124,13 @@ static LogicalResult replaceDimOrSym(AffineMap *map,
1064
1124
if (!v)
1065
1125
return failure ();
1066
1126
1127
+ auto minOp = v.getDefiningOp <AffineMinOp>();
1128
+ if (minOp && replaceAffineMin) {
1129
+ AffineExpr dimOrSym = isDimReplacement ? getAffineDimExpr (pos, ctx)
1130
+ : getAffineSymbolExpr (pos, ctx);
1131
+ return replaceAffineMinBoundingBoxExpression (minOp, dimOrSym, map);
1132
+ }
1133
+
1067
1134
auto affineApply = v.getDefiningOp <AffineApplyOp>();
1068
1135
if (!affineApply)
1069
1136
return failure ();
@@ -1101,7 +1168,8 @@ static LogicalResult replaceDimOrSym(AffineMap *map,
1101
1168
// / iteratively. Perform canonicalization of map and operands as well as
1102
1169
// / AffineMap simplification. `map` and `operands` are mutated in place.
1103
1170
static void composeAffineMapAndOperands (AffineMap *map,
1104
- SmallVectorImpl<Value> *operands) {
1171
+ SmallVectorImpl<Value> *operands,
1172
+ bool composeAffineMin = false ) {
1105
1173
if (map->getNumResults () == 0 ) {
1106
1174
canonicalizeMapAndOperands (map, operands);
1107
1175
*map = simplifyAffineMap (*map);
@@ -1122,7 +1190,8 @@ static void composeAffineMapAndOperands(AffineMap *map,
1122
1190
while (true ) {
1123
1191
bool changed = false ;
1124
1192
for (unsigned pos = 0 ; pos != dims.size () + syms.size (); ++pos)
1125
- if ((changed |= succeeded (replaceDimOrSym (map, pos, dims, syms))))
1193
+ if ((changed |=
1194
+ succeeded (replaceDimOrSym (map, pos, dims, syms, composeAffineMin))))
1126
1195
break ;
1127
1196
if (!changed)
1128
1197
break ;
@@ -1163,38 +1232,41 @@ static void composeAffineMapAndOperands(AffineMap *map,
1163
1232
}
1164
1233
1165
1234
void mlir::affine::fullyComposeAffineMapAndOperands (
1166
- AffineMap *map, SmallVectorImpl<Value> *operands) {
1235
+ AffineMap *map, SmallVectorImpl<Value> *operands, bool composeAffineMin ) {
1167
1236
while (llvm::any_of (*operands, [](Value v) {
1168
1237
return isa_and_nonnull<AffineApplyOp>(v.getDefiningOp ());
1169
1238
})) {
1170
- composeAffineMapAndOperands (map, operands);
1239
+ composeAffineMapAndOperands (map, operands, composeAffineMin );
1171
1240
}
1172
1241
}
1173
1242
1174
1243
AffineApplyOp
1175
1244
mlir::affine::makeComposedAffineApply (OpBuilder &b, Location loc, AffineMap map,
1176
- ArrayRef<OpFoldResult> operands) {
1245
+ ArrayRef<OpFoldResult> operands,
1246
+ bool composeAffineMin) {
1177
1247
SmallVector<Value> valueOperands;
1178
1248
map = foldAttributesIntoMap (b, map, operands, valueOperands);
1179
- composeAffineMapAndOperands (&map, &valueOperands);
1249
+ composeAffineMapAndOperands (&map, &valueOperands, composeAffineMin );
1180
1250
assert (map);
1181
1251
return b.create <AffineApplyOp>(loc, map, valueOperands);
1182
1252
}
1183
1253
1184
1254
AffineApplyOp
1185
1255
mlir::affine::makeComposedAffineApply (OpBuilder &b, Location loc, AffineExpr e,
1186
- ArrayRef<OpFoldResult> operands) {
1256
+ ArrayRef<OpFoldResult> operands,
1257
+ bool composeAffineMin) {
1187
1258
return makeComposedAffineApply (
1188
1259
b, loc,
1189
1260
AffineMap::inferFromExprList (ArrayRef<AffineExpr>{e}, b.getContext ())
1190
1261
.front (),
1191
- operands);
1262
+ operands, composeAffineMin );
1192
1263
}
1193
1264
1194
1265
// / Composes the given affine map with the given list of operands, pulling in
1195
1266
// / the maps from any affine.apply operations that supply the operands.
1196
1267
static void composeMultiResultAffineMap (AffineMap &map,
1197
- SmallVectorImpl<Value> &operands) {
1268
+ SmallVectorImpl<Value> &operands,
1269
+ bool composeAffineMin = false ) {
1198
1270
// Compose and canonicalize each expression in the map individually because
1199
1271
// composition only applies to single-result maps, collecting potentially
1200
1272
// duplicate operands in a single list with shifted dimensions and symbols.
@@ -1203,7 +1275,8 @@ static void composeMultiResultAffineMap(AffineMap &map,
1203
1275
for (unsigned i : llvm::seq<unsigned >(0 , map.getNumResults ())) {
1204
1276
SmallVector<Value> submapOperands (operands.begin (), operands.end ());
1205
1277
AffineMap submap = map.getSubMap ({i});
1206
- fullyComposeAffineMapAndOperands (&submap, &submapOperands);
1278
+ fullyComposeAffineMapAndOperands (&submap, &submapOperands,
1279
+ composeAffineMin);
1207
1280
canonicalizeMapAndOperands (&submap, &submapOperands);
1208
1281
unsigned numNewDims = submap.getNumDims ();
1209
1282
submap = submap.shiftDims (dims.size ()).shiftSymbols (symbols.size ());
@@ -1221,10 +1294,9 @@ static void composeMultiResultAffineMap(AffineMap &map,
1221
1294
canonicalizeMapAndOperands (&map, &operands);
1222
1295
}
1223
1296
1224
- OpFoldResult
1225
- mlir::affine::makeComposedFoldedAffineApply (OpBuilder &b, Location loc,
1226
- AffineMap map,
1227
- ArrayRef<OpFoldResult> operands) {
1297
+ OpFoldResult mlir::affine::makeComposedFoldedAffineApply (
1298
+ OpBuilder &b, Location loc, AffineMap map, ArrayRef<OpFoldResult> operands,
1299
+ bool composeAffineMin) {
1228
1300
assert (map.getNumResults () == 1 && " building affine.apply with !=1 result" );
1229
1301
1230
1302
// Create new builder without a listener, so that no notification is
@@ -1236,7 +1308,7 @@ mlir::affine::makeComposedFoldedAffineApply(OpBuilder &b, Location loc,
1236
1308
1237
1309
// Create op.
1238
1310
AffineApplyOp applyOp =
1239
- makeComposedAffineApply (newBuilder, loc, map, operands);
1311
+ makeComposedAffineApply (newBuilder, loc, map, operands, composeAffineMin );
1240
1312
1241
1313
// Get constant operands.
1242
1314
SmallVector<Attribute> constOperands (applyOp->getNumOperands ());
@@ -1256,26 +1328,25 @@ mlir::affine::makeComposedFoldedAffineApply(OpBuilder &b, Location loc,
1256
1328
return llvm::getSingleElement (foldResults);
1257
1329
}
1258
1330
1259
- OpFoldResult
1260
- mlir::affine::makeComposedFoldedAffineApply (OpBuilder &b, Location loc,
1261
- AffineExpr expr,
1262
- ArrayRef<OpFoldResult> operands) {
1331
+ OpFoldResult mlir::affine::makeComposedFoldedAffineApply (
1332
+ OpBuilder &b, Location loc, AffineExpr expr,
1333
+ ArrayRef<OpFoldResult> operands, bool composeAffineMin) {
1263
1334
return makeComposedFoldedAffineApply (
1264
1335
b, loc,
1265
1336
AffineMap::inferFromExprList (ArrayRef<AffineExpr>{expr}, b.getContext ())
1266
1337
.front (),
1267
- operands);
1338
+ operands, composeAffineMin );
1268
1339
}
1269
1340
1270
1341
SmallVector<OpFoldResult>
1271
1342
mlir::affine::makeComposedFoldedMultiResultAffineApply (
1272
- OpBuilder &b, Location loc, AffineMap map,
1273
- ArrayRef<OpFoldResult> operands ) {
1274
- return llvm::map_to_vector (llvm::seq< unsigned >( 0 , map. getNumResults ()),
1275
- [&](unsigned i) {
1276
- return makeComposedFoldedAffineApply (
1277
- b, loc, map. getSubMap ({i}), operands);
1278
- });
1343
+ OpBuilder &b, Location loc, AffineMap map, ArrayRef<OpFoldResult> operands,
1344
+ bool composeAffineMin ) {
1345
+ return llvm::map_to_vector (
1346
+ llvm::seq< unsigned >( 0 , map. getNumResults ()), [&](unsigned i) {
1347
+ return makeComposedFoldedAffineApply (b, loc, map. getSubMap ({i}),
1348
+ operands, composeAffineMin );
1349
+ });
1279
1350
}
1280
1351
1281
1352
template <typename OpTy>
@@ -3024,7 +3095,8 @@ void AffineIfOp::build(OpBuilder &builder, OperationState &result,
3024
3095
// / `set` by composing the maps of such affine.apply ops with the integer
3025
3096
// / set constraints.
3026
3097
static void composeSetAndOperands (IntegerSet &set,
3027
- SmallVectorImpl<Value> &operands) {
3098
+ SmallVectorImpl<Value> &operands,
3099
+ bool composeAffineMin) {
3028
3100
// We will simply reuse the API of the map composition by viewing the LHSs of
3029
3101
// the equalities and inequalities of `set` as the affine exprs of an affine
3030
3102
// map. Convert to equivalent map, compose, and convert back to set.
@@ -3035,7 +3107,7 @@ static void composeSetAndOperands(IntegerSet &set,
3035
3107
[](Value v) { return v.getDefiningOp <AffineApplyOp>(); }))
3036
3108
return ;
3037
3109
3038
- composeAffineMapAndOperands (&map, &operands);
3110
+ composeAffineMapAndOperands (&map, &operands, composeAffineMin );
3039
3111
set = IntegerSet::get (map.getNumDims (), map.getNumSymbols (), map.getResults (),
3040
3112
set.getEqFlags ());
3041
3113
}
@@ -3044,7 +3116,7 @@ static void composeSetAndOperands(IntegerSet &set,
3044
3116
LogicalResult AffineIfOp::fold (FoldAdaptor, SmallVectorImpl<OpFoldResult> &) {
3045
3117
auto set = getIntegerSet ();
3046
3118
SmallVector<Value, 4 > operands (getOperands ());
3047
- composeSetAndOperands (set, operands);
3119
+ composeSetAndOperands (set, operands, /* composeAffineMin= */ false );
3048
3120
canonicalizeSetAndOperands (&set, &operands);
3049
3121
3050
3122
// Check if the canonicalization or composition led to any change.
0 commit comments