[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 24 12:51:43 PDT 2025
https://github.com/ivangarcia44 updated https://github.com/llvm/llvm-project/pull/135244
>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 1/2] 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,
>From 8e9f389fdbccfb86edcd1088b3ef61807aa317d7 Mon Sep 17 00:00:00 2001
From: Ivan Garcia <igarcia at vdi-ah2ddp-178.dhcp.mathworks.com>
Date: Thu, 24 Apr 2025 15:51:07 -0400
Subject: [PATCH 2/2] Addressing feedback from Matthias Springer.
---
.../mlir/Dialect/MemRef/IR/MemRefOps.td | 233 ++++++++++--------
1 file changed, 131 insertions(+), 102 deletions(-)
diff --git a/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td b/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td
index 909b5815ef89e..06e7c9065e9d3 100644
--- a/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td
+++ b/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td
@@ -1332,54 +1332,33 @@ def MemRef_ReinterpretCastOp
Modify offset, sizes and strides of an unranked/ranked memref.
Example 1:
- ```mlir
- memref.reinterpret_cast %ranked to
- offset: [0],
- sizes: [%size0, 10],
- strides: [1, %stride1]
- : memref<?x?xf32> to memref<?x10xf32, strided<[1, ?], offset: 0>>
-
- memref.reinterpret_cast %unranked to
- offset: [%offset],
- sizes: [%size0, %size1],
- strides: [%stride0, %stride1]
- : memref<*xf32> to memref<?x?xf32, strided<[?, ?], offset: ?>>
- ```
- This operation creates a new memref descriptor using the base of the
- source and applying the input arguments to the other metadata.
- In other words:
- ```mlir
- %dst = memref.reinterpret_cast %src to
- offset: [%offset],
- sizes: [%sizes],
- strides: [%strides]
- ```
- means that `%dst`'s descriptor will be:
- ```mlir
- %dst.base = %src.base
- %dst.aligned = %src.aligned
- %dst.offset = %offset
- %dst.sizes = %sizes
- %dst.strides = %strides
- ```
-
- Example 2:
-
- Consecutive `reinterpret_cast` operations on memref's with static dimensions.
+ 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.
+ they appear in the contiguous memory of the memref — and the
+ *strided memref*, 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>>
+ %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:
+ The underlying memory of `%arg0` consists of a linear sequence of integers
+ from 1 to 64. Its memref has the following 8x8 elements:
```mlir
[[1, 2, 3, 4, 5, 6, 7, 8],
@@ -1392,7 +1371,8 @@ def MemRef_ReinterpretCastOp
[57, 58, 59, 60, 61, 62, 63, 64]]
```
- Following the first `reinterpret_cast`, the view of `%result1` is:
+ Following the first `reinterpret_cast`, the strided memref elements
+ of `%result1` are:
```mlir
[[10, 12, 14, 16],
@@ -1401,20 +1381,60 @@ def MemRef_ReinterpretCastOp
[58, 60, 62, 64]]
```
- Note: The offset and strides are relative to the underlying memory of `%arg0`.
+ Note: The offset and strides are relative to the underlying memory of
+ `%arg0`.
- The second `reinterpret_cast` results in the following view for `%result2`:
+ The second `reinterpret_cast` results in the following strided memref
+ 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.
+ Notice that it does not matter if you use %result1 or %arg0 as a source
+ for the second `reinterpret_cast` operation. Only the underlying memory
+ pointers will be reused.
+
+ 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 strided memref (refer to `subview` examples).
+ Consequently, the second `reinterpret_cast` behaves as if `%arg0` were
+ passed directly as its argument.
+
+ Example 2:
+ ```mlir
+ memref.reinterpret_cast %ranked to
+ offset: [0],
+ sizes: [%size0, 10],
+ strides: [1, %stride1]
+ : memref<?x?xf32> to memref<?x10xf32, strided<[1, ?], offset: 0>>
+
+ memref.reinterpret_cast %unranked to
+ offset: [%offset],
+ sizes: [%size0, %size1],
+ strides: [%stride0, %stride1]
+ : memref<*xf32> to memref<?x?xf32, strided<[?, ?], offset: ?>>
+ ```
+
+ This operation creates a new memref descriptor using the base of the
+ source and applying the input arguments to the other metadata.
+ In other words:
+ ```mlir
+ %dst = memref.reinterpret_cast %src to
+ offset: [%offset],
+ sizes: [%sizes],
+ strides: [%strides]
+ ```
+ means that `%dst`'s descriptor will be:
+ ```mlir
+ %dst.base = %src.base
+ %dst.aligned = %src.aligned
+ %dst.offset = %offset
+ %dst.sizes = %sizes
+ %dst.strides = %strides
+ ```
}];
let arguments = (ins Arg<AnyRankedOrUnrankedMemRef, "", []>:$source,
@@ -1950,6 +1970,64 @@ def SubViewOp : MemRef_OpWithOffsetSizesAndStrides<"subview", [
Example 1:
+ 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
+ *strided memref*, 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 underlying memory of `%arg0` consists of a linear sequence of integers
+ from 1 to 64. Its memref has the following 8x8 elements:
+
+ ```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 strided memref elements of `%result1`
+ are:
+
+ ```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 strided memref of `%arg0`
+ (compare to the corresponding `reinterpret_cast` example).
+
+ The second `subview` results in the following strided memref for
+ `%result2`:
+
+ ```mlir
+ [[28, 32],
+ [60, 64]]
+ ```
+
+ Unlike the `reinterpret_cast`, the values are relative to the strided
+ memref of the input (`%result1` in this case) and not its
+ underlying memory.
+
+ Example 2:
+
```mlir
// Subview of static memref with strided layout at static offsets, sizes
// and strides.
@@ -1958,7 +2036,7 @@ def SubViewOp : MemRef_OpWithOffsetSizesAndStrides<"subview", [
memref<8x2xf32, strided<[21, 18], offset: 137>>
```
- Example 2:
+ Example 3:
```mlir
// Subview of static memref with identity layout at dynamic offsets, sizes
@@ -1967,7 +2045,7 @@ def SubViewOp : MemRef_OpWithOffsetSizesAndStrides<"subview", [
: memref<64x4xf32> to memref<?x?xf32, strided<[?, ?], offset: ?>>
```
- Example 3:
+ Example 4:
```mlir
// Subview of dynamic memref with strided layout at dynamic offsets and
@@ -1977,7 +2055,7 @@ def SubViewOp : MemRef_OpWithOffsetSizesAndStrides<"subview", [
memref<4x4xf32, strided<[?, ?], offset: ?>>
```
- Example 4:
+ Example 5:
```mlir
// Rank-reducing subviews.
@@ -1987,62 +2065,13 @@ def SubViewOp : MemRef_OpWithOffsetSizesAndStrides<"subview", [
: memref<8x16x4xf32> to memref<6x3xf32, strided<[4, 1], offset: 210>>
```
- Example 5:
+ Example 6:
```mlir
// Identity subview. The subview is the full source memref.
%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