Skip to content

Commit 9dfcddf

Browse files
committed
[mlir] Linalg tiling: generate code avoding out-of-bounds accesses
Summary: After the `subview` operation was migrated from Linalg to Standard, it changed semantics and does not guarantee the absence of out-of-bounds accesses through the created view anymore. Compute the size of the subview to make sure it always fits within the view (subviews in last iterations of the loops may be smaller than those in other iterations). Differential Revision: https://reviews.llvm.org/D73614
1 parent d379253 commit 9dfcddf

File tree

4 files changed

+175
-29
lines changed

4 files changed

+175
-29
lines changed

mlir/include/mlir/EDSC/Intrinsics.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ using addf = ValueBuilder<AddFOp>;
195195
using affine_apply = ValueBuilder<AffineApplyOp>;
196196
using affine_if = OperationBuilder<AffineIfOp>;
197197
using affine_load = ValueBuilder<AffineLoadOp>;
198+
using affine_min = ValueBuilder<AffineMinOp>;
198199
using affine_store = OperationBuilder<AffineStoreOp>;
199200
using alloc = ValueBuilder<AllocOp>;
200201
using call = OperationBuilder<mlir::CallOp>;

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

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,8 @@ makeTiledViews(OpBuilder &b, Location loc, LinalgOp linalgOp,
260260
for (unsigned viewIndex = 0; viewIndex < linalgOp.getNumInputsAndOutputs();
261261
++viewIndex) {
262262
Value view = *(viewIteratorBegin + viewIndex);
263-
unsigned rank = view.getType().cast<MemRefType>().getRank();
263+
auto viewType = view.getType().cast<MemRefType>();
264+
unsigned rank = viewType.getRank();
264265
auto map = loopToOperandRangesMaps(linalgOp)[viewIndex];
265266
// If the view is not tiled, we can use it as is.
266267
if (!isTiled(map, tileSizes)) {
@@ -287,12 +288,29 @@ makeTiledViews(OpBuilder &b, Location loc, LinalgOp linalgOp,
287288
auto offset = applyMapToValues(b, loc, m, lbs, folder).front();
288289
offsets.push_back(offset);
289290
auto size = applyMapToValues(b, loc, m, subViewSizes, folder).front();
291+
292+
// The size of the subview should be trimmed to avoid out-of-bounds
293+
// accesses, unless we statically know the subview size divides the view
294+
// size evenly.
295+
int64_t viewSize = viewType.getDimSize(r);
296+
auto sizeCst = dyn_cast_or_null<ConstantIndexOp>(size.getDefiningOp());
297+
if (ShapedType::isDynamic(viewSize) || !sizeCst ||
298+
(viewSize % sizeCst.getValue()) != 0) {
299+
// Compute min(size, dim - offset) to avoid out-of-bounds accesses.
300+
auto minMap = AffineMap::get(
301+
/*dimCount=*/3, /*symbolCount=*/0,
302+
{getAffineDimExpr(/*position=*/0, b.getContext()),
303+
getAffineDimExpr(/*position=*/1, b.getContext()) -
304+
getAffineDimExpr(/*position=*/2, b.getContext())});
305+
auto d = dim(folder, view, r);
306+
size = affine_min(folder, b.getIndexType(), minMap,
307+
ValueRange{size, d, offset});
308+
}
309+
290310
sizes.push_back(size);
291311
strides.push_back(constant_index(folder, 1));
292312
}
293-
// TODO(b/144419024) Atm std.subview is not guaranteed in-bounds. Depending
294-
// on the semantics we attach to it, we may need to use min(size, dim) here
295-
// and canonicalize later.
313+
296314
res.push_back(b.create<SubViewOp>(loc, view, offsets, sizes, strides));
297315
}
298316

0 commit comments

Comments
 (0)