Skip to content

Commit a664f58

Browse files
HanumanthHanumanth Hanumantharayappa
andauthored
[mlir][memref] Fix runtime verification for memref.subview for empty memref subviews (llvm#166581)
This PR applies the same fix from llvm#166569 to `memref.subview`. That PR fixed the issue for `tensor.extract_slice`, and this one addresses the identical problem for `memref.subview`. The runtime verification for `memref.subview` incorrectly rejects valid empty subviews (size=0) starting at the memref boundary. **Example that demonstrates the issue:** ```mlir func.func @subview_with_empty_slice(%memref: memref<10x4x1xf32, strided<[?, ?, ?], offset: ?>>, %dim_0: index, %dim_1: index, %dim_2: index, %offset: index) { // When called with: offset=10, dim_0=0, dim_1=4, dim_2=1 // Runtime verification fails: "offset 0 is out-of-bounds" %subview = memref.subview %memref[%offset, 0, 0] [%dim_0, %dim_1, %dim_2] [1, 1, 1] : memref<10x4x1xf32, strided<[?, ?, ?], offset: ?>> to memref<?x?x?xf32, strided<[?, ?, ?], offset: ?>> return } ``` When `%offset=10` and `%dim_0=0`, we're creating an empty subview (zero elements along dimension 0) starting at the boundary. The current verification enforces `offset < dim_size`, which evaluates to `10 < 10` and fails. I feel this should be valid since no memory is accessed. **The fix:** Same as llvm#166569 - make the offset check conditional on subview size: - Empty subview (size == 0): allow `0 <= offset <= dim_size` - Non-empty subview (size > 0): require `0 <= offset < dim_size` Please see llvm#166569 for motivation and rationale. --- Co-authored-by: Hanumanth Hanumantharayappa <[email protected]>
1 parent e3a9ac5 commit a664f58

File tree

2 files changed

+70
-34
lines changed

2 files changed

+70
-34
lines changed

mlir/lib/Dialect/MemRef/Transforms/RuntimeOpVerification.cpp

Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -268,61 +268,82 @@ struct SubViewOpInterface
268268
MemRefType sourceType = subView.getSource().getType();
269269

270270
// For each dimension, assert that:
271-
// 0 <= offset < dim_size
272-
// 0 <= offset + (size - 1) * stride < dim_size
271+
// For empty slices (size == 0) : 0 <= offset <= dim_size
272+
// For non-empty slices (size > 0): 0 <= offset < dim_size
273+
// 0 <= offset + (size - 1) * stride
274+
// dim_size
273275
Value zero = arith::ConstantIndexOp::create(builder, loc, 0);
274276
Value one = arith::ConstantIndexOp::create(builder, loc, 1);
277+
275278
auto metadataOp =
276279
ExtractStridedMetadataOp::create(builder, loc, subView.getSource());
280+
277281
for (int64_t i : llvm::seq<int64_t>(0, sourceType.getRank())) {
278-
// Reset insertion point to before the operation for each dimension
282+
// Reset insertion point to before the operation for each dimension.
279283
builder.setInsertionPoint(subView);
284+
280285
Value offset = getValueOrCreateConstantIndexOp(
281286
builder, loc, subView.getMixedOffsets()[i]);
282287
Value size = getValueOrCreateConstantIndexOp(builder, loc,
283288
subView.getMixedSizes()[i]);
284289
Value stride = getValueOrCreateConstantIndexOp(
285290
builder, loc, subView.getMixedStrides()[i]);
286-
287-
// Verify that offset is in-bounds.
288291
Value dimSize = metadataOp.getSizes()[i];
289-
Value offsetInBounds =
290-
generateInBoundsCheck(builder, loc, offset, zero, dimSize);
291-
cf::AssertOp::create(builder, loc, offsetInBounds,
292+
293+
// Verify that offset is in-bounds (conditional on slice size).
294+
Value sizeIsZero = arith::CmpIOp::create(
295+
builder, loc, arith::CmpIPredicate::eq, size, zero);
296+
auto offsetCheckIf = scf::IfOp::create(
297+
builder, loc, sizeIsZero,
298+
[&](OpBuilder &b, Location loc) {
299+
// For empty slices, offset can be at the boundary: 0 <= offset <=
300+
// dimSize.
301+
Value offsetGEZero = arith::CmpIOp::create(
302+
b, loc, arith::CmpIPredicate::sge, offset, zero);
303+
Value offsetLEDimSize = arith::CmpIOp::create(
304+
b, loc, arith::CmpIPredicate::sle, offset, dimSize);
305+
Value emptyOffsetValid =
306+
arith::AndIOp::create(b, loc, offsetGEZero, offsetLEDimSize);
307+
scf::YieldOp::create(b, loc, emptyOffsetValid);
308+
},
309+
[&](OpBuilder &b, Location loc) {
310+
// For non-empty slices, offset must be a valid index: 0 <= offset
311+
// dimSize.
312+
Value offsetInBounds =
313+
generateInBoundsCheck(b, loc, offset, zero, dimSize);
314+
scf::YieldOp::create(b, loc, offsetInBounds);
315+
});
316+
317+
Value offsetCondition = offsetCheckIf.getResult(0);
318+
cf::AssertOp::create(builder, loc, offsetCondition,
292319
generateErrorMessage(op, "offset " +
293320
std::to_string(i) +
294321
" is out-of-bounds"));
295322

296-
// Only verify if size > 0
323+
// Verify that the slice endpoint is in-bounds (only for non-empty
324+
// slices).
297325
Value sizeIsNonZero = arith::CmpIOp::create(
298326
builder, loc, arith::CmpIPredicate::sgt, size, zero);
327+
auto ifOp = scf::IfOp::create(
328+
builder, loc, sizeIsNonZero,
329+
[&](OpBuilder &b, Location loc) {
330+
// Verify that slice does not run out-of-bounds.
331+
Value sizeMinusOne = arith::SubIOp::create(b, loc, size, one);
332+
Value sizeMinusOneTimesStride =
333+
arith::MulIOp::create(b, loc, sizeMinusOne, stride);
334+
Value lastPos =
335+
arith::AddIOp::create(b, loc, offset, sizeMinusOneTimesStride);
336+
Value lastPosInBounds =
337+
generateInBoundsCheck(b, loc, lastPos, zero, dimSize);
338+
scf::YieldOp::create(b, loc, lastPosInBounds);
339+
},
340+
[&](OpBuilder &b, Location loc) {
341+
Value trueVal =
342+
arith::ConstantOp::create(b, loc, b.getBoolAttr(true));
343+
scf::YieldOp::create(b, loc, trueVal);
344+
});
299345

300-
auto ifOp = scf::IfOp::create(builder, loc, builder.getI1Type(),
301-
sizeIsNonZero, /*withElseRegion=*/true);
302-
303-
// Populate the "then" region (for size > 0).
304-
builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
305-
306-
// Verify that slice does not run out-of-bounds.
307-
Value sizeMinusOne = arith::SubIOp::create(builder, loc, size, one);
308-
Value sizeMinusOneTimesStride =
309-
arith::MulIOp::create(builder, loc, sizeMinusOne, stride);
310-
Value lastPos =
311-
arith::AddIOp::create(builder, loc, offset, sizeMinusOneTimesStride);
312-
Value lastPosInBounds =
313-
generateInBoundsCheck(builder, loc, lastPos, zero, dimSize);
314-
315-
scf::YieldOp::create(builder, loc, lastPosInBounds);
316-
317-
// Populate the "else" region (for size == 0).
318-
builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
319-
Value trueVal =
320-
arith::ConstantOp::create(builder, loc, builder.getBoolAttr(true));
321-
scf::YieldOp::create(builder, loc, trueVal);
322-
323-
builder.setInsertionPointAfter(ifOp);
324346
Value finalCondition = ifOp.getResult(0);
325-
326347
cf::AssertOp::create(
327348
builder, loc, finalCondition,
328349
generateErrorMessage(op,

mlir/test/Integration/Dialect/MemRef/subview-runtime-verification.mlir

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ func.func @subview_zero_size_dim(%memref: memref<10x4x1xf32, strided<[?, ?, ?],
5050
return
5151
}
5252

53+
func.func @subview_with_empty_slice(%memref: memref<10x4x1xf32, strided<[?, ?, ?], offset: ?>>,
54+
%dim_0: index,
55+
%dim_1: index,
56+
%dim_2: index,
57+
%offset: index) {
58+
%subview = memref.subview %memref[%offset, 0, 0] [%dim_0, %dim_1, %dim_2] [1, 1, 1] :
59+
memref<10x4x1xf32, strided<[?, ?, ?], offset: ?>> to
60+
memref<?x?x?xf32, strided<[?, ?, ?], offset: ?>>
61+
return
62+
}
63+
5364

5465
func.func @main() {
5566
%0 = arith.constant 0 : index
@@ -127,5 +138,9 @@ func.func @main() {
127138
func.call @subview_zero_size_dim(%alloca_10x4x1_dyn_stride, %dim_0, %dim_1, %dim_2)
128139
: (memref<10x4x1xf32, strided<[?, ?, ?], offset: ?>>, index, index, index) -> ()
129140

141+
// CHECK-NOT: ERROR: Runtime op verification failed
142+
%offset = arith.constant 10 : index
143+
func.call @subview_with_empty_slice(%alloca_10x4x1_dyn_stride, %dim_0, %dim_1, %dim_2, %offset)
144+
: (memref<10x4x1xf32, strided<[?, ?, ?], offset: ?>>, index, index, index, index) -> ()
130145
return
131146
}

0 commit comments

Comments
 (0)