[Mlir-commits] [mlir] Add examples for reinterpret_cast and subview operators to show their behavior in relation to their input memref underlying memory and view (PR #135244)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Thu Apr 10 12:59:21 PDT 2025
https://github.com/ivangarcia44 created https://github.com/llvm/llvm-project/pull/135244
While working on #134845 I was trying to understand the difference of how the reinterpret_cast and subview operators see the input memref, but it was not clear to me.
I did a couple of experiments in which I learned that the subview takes into account the view of the input memref to create the view of the output memref, while the reinterpret_cast just uses the underlying memory of the input memref.
I thought it would help future readers to see these two experiements as examples in the documentation to quickly figure out the difference between these two operators.
>From 2dac9bb56ad2afd251a44296983687c5a1ce320c Mon Sep 17 00:00:00 2001
From: Ivan Garcia <igarcia at vdi-ah2ddp-178.dhcp.mathworks.com>
Date: Thu, 10 Apr 2025 15:52:31 -0400
Subject: [PATCH] Add examples for reinterpret_cast and subview operators to
show their behavior in relation to their input memref underlying memory and
view.
---
.../mlir/Dialect/MemRef/IR/MemRefOps.td | 102 +++++++++++++++++-
1 file changed, 101 insertions(+), 1 deletion(-)
diff --git a/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td b/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td
index 3edc2433c85ea..909b5815ef89e 100644
--- a/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td
+++ b/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td
@@ -1331,7 +1331,7 @@ def MemRef_ReinterpretCastOp
let description = [{
Modify offset, sizes and strides of an unranked/ranked memref.
- Example:
+ Example 1:
```mlir
memref.reinterpret_cast %ranked to
offset: [0],
@@ -1363,6 +1363,58 @@ def MemRef_ReinterpretCastOp
%dst.sizes = %sizes
%dst.strides = %strides
```
+
+ Example 2:
+
+ Consecutive `reinterpret_cast` operations on memref's with static dimensions.
+
+ We distinguish between *underlying memory* — the sequence of elements as
+ they appear in the contiguous memory of the memref — and the *view*, which refers to
+ the underlying memory interpreted according to specified offsets, sizes, and strides.
+
+ ```mlir
+ %result1 = memref.reinterpret_cast %arg0 to offset: [9], sizes: [4, 4], strides: [16, 2] : memref<8x8xf32, strided<[8, 1], offset: 0>> to memref<4x4xf32, strided<[16, 2], offset: 9>>
+
+ %result2 = memref.reinterpret_cast %result1 to offset: [0], sizes: [2, 2], strides: [4, 2] : memref<4x4xf32, strided<[16, 2], offset: 9>> to memref<2x2xf32, strided<[4, 2], offset: 0>>
+ ```
+
+ The input memref `%arg0` has the following view. The underlying memory consists
+ of a linear sequence of integers from 1 to 64:
+
+ ```mlir
+ [[1, 2, 3, 4, 5, 6, 7, 8],
+ [9, 10, 11, 12, 13, 14, 15, 16],
+ [17, 18, 19, 20, 21, 22, 23, 24],
+ [25, 26, 27, 28, 29, 30, 31, 32],
+ [33, 34, 35, 36, 37, 38, 39, 40],
+ [41, 42, 43, 44, 45, 46, 47, 48],
+ [49, 50, 51, 52, 53, 54, 55, 56],
+ [57, 58, 59, 60, 61, 62, 63, 64]]
+ ```
+
+ Following the first `reinterpret_cast`, the view of `%result1` is:
+
+ ```mlir
+ [[10, 12, 14, 16],
+ [26, 28, 30, 32],
+ [42, 44, 46, 48],
+ [58, 60, 62, 64]]
+ ```
+
+ Note: The offset and strides are relative to the underlying memory of `%arg0`.
+
+ The second `reinterpret_cast` results in the following view for `%result2`:
+
+ ```mlir
+ [[1, 3],
+ [5, 7]]
+ ```
+
+ It is important to observe that the offset and stride are relative to the base underlying
+ memory of the memref, starting at 1, not at 10 as seen in the output of `%result1`.
+ This behavior contrasts with the `subview` operator, where values are relative to the view of
+ the memref (refer to `subview` examples). Consequently, the second `reinterpret_cast` behaves
+ as if `%arg0` were passed directly as its argument.
}];
let arguments = (ins Arg<AnyRankedOrUnrankedMemRef, "", []>:$source,
@@ -1942,7 +1994,55 @@ def SubViewOp : MemRef_OpWithOffsetSizesAndStrides<"subview", [
%1 = memref.subview %0[0, 0, 0] [8, 16, 4] [1, 1, 1]
: memref<8x16x4xf32> to memref<8x16x4xf32>
```
+ Example 6:
+
+ Consecutive `subview` operations on memref's with static dimensions.
+
+ We distinguish between *underlying memory* — the sequence of elements as
+ they appear in the contiguous memory of the memref — and the *view*, which refers to
+ the underlying memory interpreted according to specified offsets, sizes, and strides.
+
+ ```mlir
+ %result1 = memref.subview %arg0[1, 1][4, 4][2, 2] : memref<8x8xf32, strided<[8, 1], offset: 0>> to memref<4x4xf32, strided<[16, 2], offset: 9>>
+
+ %result2 = memref.subview %result1[1, 1][2, 2][2, 2] : memref<4x4xf32, strided<[16, 2], offset: 9>> to memref<2x2xf32, strided<[32, 4], offset: 27>>
+ ```
+
+ The input memref `%arg0` has the following view. The underlying memory for this input
+ memref is a linear sequence of integers from 1 to 64:
+
+ ```mlir
+ [[1, 2, 3, 4, 5, 6, 7, 8],
+ [9, 10, 11, 12, 13, 14, 15, 16],
+ [17, 18, 19, 20, 21, 22, 23, 24],
+ [25, 26, 27, 28, 29, 30, 31, 32],
+ [33, 34, 35, 36, 37, 38, 39, 40],
+ [41, 42, 43, 44, 45, 46, 47, 48],
+ [49, 50, 51, 52, 53, 54, 55, 56],
+ [57, 58, 59, 60, 61, 62, 63, 64]]
+ ```
+
+ Following the first `subview`, the view of `%result1` is:
+
+ ```mlir
+ [[10, 12, 14, 16],
+ [26, 28, 30, 32],
+ [42, 44, 46, 48],
+ [58, 60, 62, 64]]
+ ```
+
+ Note: The offset and strides are relative to the memref view of `%arg0` (compare to the
+ corresponding `reinterpret_cast` example).
+
+ The second `subview` results in the following view for `%result2`:
+
+ ```mlir
+ [[28, 32],
+ [60, 64]]
+ ```
+ Unlike the `reinterpret_cast`, the values are relative to the view of the input memref
+ (`%result1` in this case) and not its underlying memory.
}];
let arguments = (ins AnyMemRef:$source,
More information about the Mlir-commits
mailing list