[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