Skip to content

Commit d9a99af

Browse files
[mlir][transform] Plumb a simplified form of AffineMin folding into t… (#145170)
…ransform.pad-tiling-interface This revision introduces a simple variant of AffineMin folding in makeComposedFoldedAffineApply and makes use of it in transform.pad-tiling-interface. Since this version explicitly call ValueBoundsInterface, it may be too expensive and is only activate behind a flag. It results in better foldings when mixing tiling and padding, including with dynamic shapes.
1 parent 02d2a16 commit d9a99af

File tree

4 files changed

+273
-49
lines changed

4 files changed

+273
-49
lines changed

mlir/include/mlir/Dialect/Affine/IR/AffineOps.h

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -410,9 +410,11 @@ void canonicalizeSetAndOperands(IntegerSet *set,
410410
/// other AffineApplyOps supplying those operands. The operands of the resulting
411411
/// AffineApplyOp do not change the length of AffineApplyOp chains.
412412
AffineApplyOp makeComposedAffineApply(OpBuilder &b, Location loc, AffineMap map,
413-
ArrayRef<OpFoldResult> operands);
413+
ArrayRef<OpFoldResult> operands,
414+
bool composeAffineMin = false);
414415
AffineApplyOp makeComposedAffineApply(OpBuilder &b, Location loc, AffineExpr e,
415-
ArrayRef<OpFoldResult> operands);
416+
ArrayRef<OpFoldResult> operands,
417+
bool composeAffineMin = false);
416418

417419
/// Constructs an AffineApplyOp that applies `map` to `operands` after composing
418420
/// the map with the maps of any other AffineApplyOp supplying the operands,
@@ -421,16 +423,19 @@ AffineApplyOp makeComposedAffineApply(OpBuilder &b, Location loc, AffineExpr e,
421423
/// map.
422424
OpFoldResult makeComposedFoldedAffineApply(OpBuilder &b, Location loc,
423425
AffineMap map,
424-
ArrayRef<OpFoldResult> operands);
426+
ArrayRef<OpFoldResult> operands,
427+
bool composeAffineMin = false);
425428
/// Variant of `makeComposedFoldedAffineApply` that applies to an expression.
426429
OpFoldResult makeComposedFoldedAffineApply(OpBuilder &b, Location loc,
427430
AffineExpr expr,
428-
ArrayRef<OpFoldResult> operands);
431+
ArrayRef<OpFoldResult> operands,
432+
bool composeAffineMin = false);
429433
/// Variant of `makeComposedFoldedAffineApply` suitable for multi-result maps.
430434
/// Note that this may create as many affine.apply operations as the map has
431435
/// results given that affine.apply must be single-result.
432436
SmallVector<OpFoldResult> makeComposedFoldedMultiResultAffineApply(
433-
OpBuilder &b, Location loc, AffineMap map, ArrayRef<OpFoldResult> operands);
437+
OpBuilder &b, Location loc, AffineMap map, ArrayRef<OpFoldResult> operands,
438+
bool composeAffineMin = false);
434439

435440
/// Returns an AffineMinOp obtained by composing `map` and `operands` with
436441
/// AffineApplyOps supplying those operands.
@@ -459,7 +464,8 @@ OpFoldResult makeComposedFoldedAffineMax(OpBuilder &b, Location loc,
459464
/// terminal symbol, i.e., a symbol defined at the top level or a block/function
460465
/// argument.
461466
void fullyComposeAffineMapAndOperands(AffineMap *map,
462-
SmallVectorImpl<Value> *operands);
467+
SmallVectorImpl<Value> *operands,
468+
bool composeAffineMin = false);
463469

464470
} // namespace affine
465471
} // namespace mlir

mlir/lib/Dialect/Affine/IR/AffineOps.cpp

Lines changed: 105 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
#include "mlir/Dialect/MemRef/IR/MemRef.h"
1212
#include "mlir/Dialect/UB/IR/UBOps.h"
1313
#include "mlir/Dialect/Utils/StaticValueUtils.h"
14+
#include "mlir/IR/AffineExpr.h"
1415
#include "mlir/IR/AffineExprVisitor.h"
1516
#include "mlir/IR/IRMapping.h"
1617
#include "mlir/IR/IntegerSet.h"
1718
#include "mlir/IR/Matchers.h"
1819
#include "mlir/IR/OpDefinition.h"
1920
#include "mlir/IR/PatternMatch.h"
21+
#include "mlir/IR/Value.h"
2022
#include "mlir/Interfaces/ShapedOpInterfaces.h"
2123
#include "mlir/Interfaces/ValueBoundsOpInterface.h"
2224
#include "mlir/Transforms/InliningUtils.h"
@@ -26,7 +28,9 @@
2628
#include "llvm/ADT/SmallVectorExtras.h"
2729
#include "llvm/ADT/TypeSwitch.h"
2830
#include "llvm/Support/Debug.h"
31+
#include "llvm/Support/LogicalResult.h"
2932
#include "llvm/Support/MathExtras.h"
33+
#include <limits>
3034
#include <numeric>
3135
#include <optional>
3236

@@ -1042,6 +1046,62 @@ simplifyMapWithOperands(AffineMap &map, ArrayRef<Value> operands) {
10421046
map.getContext());
10431047
}
10441048

1049+
/// Assuming `dimOrSym` is a quantity in `map` that is defined by `minOp`.
1050+
/// Assuming that the quantity is of the form:
1051+
/// `affine_min(f(x, y), symbolic_cst)`.
1052+
/// This function checks that `0 < affine_min(f(x, y), symbolic_cst)` and
1053+
/// proceeds with replacing the patterns:
1054+
/// ```
1055+
/// dimOrSym.ceildiv(symbolic_cst)
1056+
/// (dimOrSym + symbolic_cst - 1).floordiv(symbolic_cst)
1057+
/// ```
1058+
/// by `1`.
1059+
///
1060+
/// Additionally, allows the caller to pass `affineMinKnownToBeNonNegative` to
1061+
/// inject static information that may not be statically discoverable.
1062+
///
1063+
/// Warning: ValueBoundsConstraintSet::computeConstantBound is needed to check
1064+
/// for the nonnegative case, if `affineMinKnownToBeNonNegative` is false.
1065+
static LogicalResult replaceAffineMinBoundingBoxExpression(
1066+
AffineMinOp minOp, AffineExpr dimOrSym, AffineMap *map,
1067+
bool affineMinKnownToBeNonNegative = false) {
1068+
auto affineMinMap = minOp.getAffineMap();
1069+
if (!affineMinKnownToBeNonNegative) {
1070+
ValueRange values = minOp->getOperands();
1071+
for (unsigned i = 0, e = affineMinMap.getNumResults(); i < e; ++i) {
1072+
AffineMap row = affineMinMap.getSubMap(ArrayRef<unsigned>{i});
1073+
FailureOr<int64_t> lowerBound =
1074+
ValueBoundsConstraintSet::computeConstantBound(
1075+
presburger::BoundType::LB, {row, values},
1076+
/*stopCondition=*/nullptr,
1077+
/*closedUB=*/true);
1078+
if (failed(lowerBound) || lowerBound.value() <= 0)
1079+
return failure();
1080+
}
1081+
}
1082+
1083+
AffineMap initialMap = *map;
1084+
for (unsigned i = 0, e = affineMinMap.getNumResults(); i != e; ++i) {
1085+
auto m = affineMinMap.getSubMap(ArrayRef<unsigned>{i});
1086+
AffineExpr expr = m.getResult(0);
1087+
if (!expr.isSymbolicOrConstant())
1088+
continue;
1089+
1090+
DenseMap<AffineExpr, AffineExpr> repl;
1091+
// dimOrSym.ceilDiv(expr) -> 1
1092+
repl[dimOrSym.ceilDiv(expr)] = getAffineConstantExpr(1, minOp.getContext());
1093+
// (dimOrSym + expr - 1).floorDiv(expr) -> 1
1094+
repl[(dimOrSym + expr - 1).floorDiv(expr)] =
1095+
getAffineConstantExpr(1, minOp.getContext());
1096+
auto newMap = map->replace(repl);
1097+
if (newMap == *map)
1098+
continue;
1099+
*map = newMap;
1100+
}
1101+
1102+
return success(*map != initialMap);
1103+
}
1104+
10451105
/// Replace all occurrences of AffineExpr at position `pos` in `map` by the
10461106
/// defining AffineApplyOp expression and operands.
10471107
/// When `dimOrSymbolPosition < dims.size()`, AffineDimExpr@[pos] is replaced.
@@ -1052,10 +1112,13 @@ simplifyMapWithOperands(AffineMap &map, ArrayRef<Value> operands) {
10521112
/// 2. `map` dim and symbols are gradually shifted to higher positions.
10531113
/// 3. Old `dim` and `sym` entries are replaced by nullptr
10541114
/// This avoids the need for any bookkeeping.
1115+
/// If `replaceAffineMin` is set to true, additionally triggers more expensive
1116+
/// replacements involving affine_min operations.
10551117
static LogicalResult replaceDimOrSym(AffineMap *map,
10561118
unsigned dimOrSymbolPosition,
10571119
SmallVectorImpl<Value> &dims,
1058-
SmallVectorImpl<Value> &syms) {
1120+
SmallVectorImpl<Value> &syms,
1121+
bool replaceAffineMin) {
10591122
MLIRContext *ctx = map->getContext();
10601123
bool isDimReplacement = (dimOrSymbolPosition < dims.size());
10611124
unsigned pos = isDimReplacement ? dimOrSymbolPosition
@@ -1064,6 +1127,13 @@ static LogicalResult replaceDimOrSym(AffineMap *map,
10641127
if (!v)
10651128
return failure();
10661129

1130+
auto minOp = v.getDefiningOp<AffineMinOp>();
1131+
if (minOp && replaceAffineMin) {
1132+
AffineExpr dimOrSym = isDimReplacement ? getAffineDimExpr(pos, ctx)
1133+
: getAffineSymbolExpr(pos, ctx);
1134+
return replaceAffineMinBoundingBoxExpression(minOp, dimOrSym, map);
1135+
}
1136+
10671137
auto affineApply = v.getDefiningOp<AffineApplyOp>();
10681138
if (!affineApply)
10691139
return failure();
@@ -1101,7 +1171,8 @@ static LogicalResult replaceDimOrSym(AffineMap *map,
11011171
/// iteratively. Perform canonicalization of map and operands as well as
11021172
/// AffineMap simplification. `map` and `operands` are mutated in place.
11031173
static void composeAffineMapAndOperands(AffineMap *map,
1104-
SmallVectorImpl<Value> *operands) {
1174+
SmallVectorImpl<Value> *operands,
1175+
bool composeAffineMin = false) {
11051176
if (map->getNumResults() == 0) {
11061177
canonicalizeMapAndOperands(map, operands);
11071178
*map = simplifyAffineMap(*map);
@@ -1122,7 +1193,8 @@ static void composeAffineMapAndOperands(AffineMap *map,
11221193
while (true) {
11231194
bool changed = false;
11241195
for (unsigned pos = 0; pos != dims.size() + syms.size(); ++pos)
1125-
if ((changed |= succeeded(replaceDimOrSym(map, pos, dims, syms))))
1196+
if ((changed |=
1197+
succeeded(replaceDimOrSym(map, pos, dims, syms, composeAffineMin))))
11261198
break;
11271199
if (!changed)
11281200
break;
@@ -1163,38 +1235,41 @@ static void composeAffineMapAndOperands(AffineMap *map,
11631235
}
11641236

11651237
void mlir::affine::fullyComposeAffineMapAndOperands(
1166-
AffineMap *map, SmallVectorImpl<Value> *operands) {
1238+
AffineMap *map, SmallVectorImpl<Value> *operands, bool composeAffineMin) {
11671239
while (llvm::any_of(*operands, [](Value v) {
11681240
return isa_and_nonnull<AffineApplyOp>(v.getDefiningOp());
11691241
})) {
1170-
composeAffineMapAndOperands(map, operands);
1242+
composeAffineMapAndOperands(map, operands, composeAffineMin);
11711243
}
11721244
}
11731245

11741246
AffineApplyOp
11751247
mlir::affine::makeComposedAffineApply(OpBuilder &b, Location loc, AffineMap map,
1176-
ArrayRef<OpFoldResult> operands) {
1248+
ArrayRef<OpFoldResult> operands,
1249+
bool composeAffineMin) {
11771250
SmallVector<Value> valueOperands;
11781251
map = foldAttributesIntoMap(b, map, operands, valueOperands);
1179-
composeAffineMapAndOperands(&map, &valueOperands);
1252+
composeAffineMapAndOperands(&map, &valueOperands, composeAffineMin);
11801253
assert(map);
11811254
return b.create<AffineApplyOp>(loc, map, valueOperands);
11821255
}
11831256

11841257
AffineApplyOp
11851258
mlir::affine::makeComposedAffineApply(OpBuilder &b, Location loc, AffineExpr e,
1186-
ArrayRef<OpFoldResult> operands) {
1259+
ArrayRef<OpFoldResult> operands,
1260+
bool composeAffineMin) {
11871261
return makeComposedAffineApply(
11881262
b, loc,
11891263
AffineMap::inferFromExprList(ArrayRef<AffineExpr>{e}, b.getContext())
11901264
.front(),
1191-
operands);
1265+
operands, composeAffineMin);
11921266
}
11931267

11941268
/// Composes the given affine map with the given list of operands, pulling in
11951269
/// the maps from any affine.apply operations that supply the operands.
11961270
static void composeMultiResultAffineMap(AffineMap &map,
1197-
SmallVectorImpl<Value> &operands) {
1271+
SmallVectorImpl<Value> &operands,
1272+
bool composeAffineMin = false) {
11981273
// Compose and canonicalize each expression in the map individually because
11991274
// composition only applies to single-result maps, collecting potentially
12001275
// duplicate operands in a single list with shifted dimensions and symbols.
@@ -1203,7 +1278,8 @@ static void composeMultiResultAffineMap(AffineMap &map,
12031278
for (unsigned i : llvm::seq<unsigned>(0, map.getNumResults())) {
12041279
SmallVector<Value> submapOperands(operands.begin(), operands.end());
12051280
AffineMap submap = map.getSubMap({i});
1206-
fullyComposeAffineMapAndOperands(&submap, &submapOperands);
1281+
fullyComposeAffineMapAndOperands(&submap, &submapOperands,
1282+
composeAffineMin);
12071283
canonicalizeMapAndOperands(&submap, &submapOperands);
12081284
unsigned numNewDims = submap.getNumDims();
12091285
submap = submap.shiftDims(dims.size()).shiftSymbols(symbols.size());
@@ -1221,10 +1297,9 @@ static void composeMultiResultAffineMap(AffineMap &map,
12211297
canonicalizeMapAndOperands(&map, &operands);
12221298
}
12231299

1224-
OpFoldResult
1225-
mlir::affine::makeComposedFoldedAffineApply(OpBuilder &b, Location loc,
1226-
AffineMap map,
1227-
ArrayRef<OpFoldResult> operands) {
1300+
OpFoldResult mlir::affine::makeComposedFoldedAffineApply(
1301+
OpBuilder &b, Location loc, AffineMap map, ArrayRef<OpFoldResult> operands,
1302+
bool composeAffineMin) {
12281303
assert(map.getNumResults() == 1 && "building affine.apply with !=1 result");
12291304

12301305
// Create new builder without a listener, so that no notification is
@@ -1236,7 +1311,7 @@ mlir::affine::makeComposedFoldedAffineApply(OpBuilder &b, Location loc,
12361311

12371312
// Create op.
12381313
AffineApplyOp applyOp =
1239-
makeComposedAffineApply(newBuilder, loc, map, operands);
1314+
makeComposedAffineApply(newBuilder, loc, map, operands, composeAffineMin);
12401315

12411316
// Get constant operands.
12421317
SmallVector<Attribute> constOperands(applyOp->getNumOperands());
@@ -1256,26 +1331,25 @@ mlir::affine::makeComposedFoldedAffineApply(OpBuilder &b, Location loc,
12561331
return llvm::getSingleElement(foldResults);
12571332
}
12581333

1259-
OpFoldResult
1260-
mlir::affine::makeComposedFoldedAffineApply(OpBuilder &b, Location loc,
1261-
AffineExpr expr,
1262-
ArrayRef<OpFoldResult> operands) {
1334+
OpFoldResult mlir::affine::makeComposedFoldedAffineApply(
1335+
OpBuilder &b, Location loc, AffineExpr expr,
1336+
ArrayRef<OpFoldResult> operands, bool composeAffineMin) {
12631337
return makeComposedFoldedAffineApply(
12641338
b, loc,
12651339
AffineMap::inferFromExprList(ArrayRef<AffineExpr>{expr}, b.getContext())
12661340
.front(),
1267-
operands);
1341+
operands, composeAffineMin);
12681342
}
12691343

12701344
SmallVector<OpFoldResult>
12711345
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-
});
1346+
OpBuilder &b, Location loc, AffineMap map, ArrayRef<OpFoldResult> operands,
1347+
bool composeAffineMin) {
1348+
return llvm::map_to_vector(
1349+
llvm::seq<unsigned>(0, map.getNumResults()), [&](unsigned i) {
1350+
return makeComposedFoldedAffineApply(b, loc, map.getSubMap({i}),
1351+
operands, composeAffineMin);
1352+
});
12791353
}
12801354

12811355
template <typename OpTy>
@@ -3024,7 +3098,8 @@ void AffineIfOp::build(OpBuilder &builder, OperationState &result,
30243098
/// `set` by composing the maps of such affine.apply ops with the integer
30253099
/// set constraints.
30263100
static void composeSetAndOperands(IntegerSet &set,
3027-
SmallVectorImpl<Value> &operands) {
3101+
SmallVectorImpl<Value> &operands,
3102+
bool composeAffineMin = false) {
30283103
// We will simply reuse the API of the map composition by viewing the LHSs of
30293104
// the equalities and inequalities of `set` as the affine exprs of an affine
30303105
// map. Convert to equivalent map, compose, and convert back to set.
@@ -3035,7 +3110,7 @@ static void composeSetAndOperands(IntegerSet &set,
30353110
[](Value v) { return v.getDefiningOp<AffineApplyOp>(); }))
30363111
return;
30373112

3038-
composeAffineMapAndOperands(&map, &operands);
3113+
composeAffineMapAndOperands(&map, &operands, composeAffineMin);
30393114
set = IntegerSet::get(map.getNumDims(), map.getNumSymbols(), map.getResults(),
30403115
set.getEqFlags());
30413116
}

mlir/lib/Dialect/Linalg/Transforms/PadTilingInterface.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ SmallVector<OpFoldResult> linalg::computePaddedShape(
8484
getDimsToSize(rewriter, indexingSizes, options);
8585

8686
// For each dimension in the operand's shape, iterate over indexingSizes and
87-
// add
87+
// add the various term contributions.
8888
for (const auto &enResults : enumerate(indexingMap.getResults())) {
8989
int64_t resultIndex = enResults.index();
9090
AffineMap partialIndexingMap = indexingMap.getSubMap(
@@ -122,7 +122,8 @@ SmallVector<OpFoldResult> linalg::computePaddedShape(
122122
AffineMap composedMap = projectedMap.compose(ceilMap);
123123
OpFoldResult paddingDimOfr = affine::makeComposedFoldedAffineApply(
124124
rewriter, loc, composedMap,
125-
{indexingSizes[paddingDim], paddingSize});
125+
{indexingSizes[paddingDim], paddingSize},
126+
/*composeAffineMin=*/true);
126127
terms.push_back(paddingDimOfr);
127128
} else {
128129
// Otherwise just set to paddingSize.

0 commit comments

Comments
 (0)