1// RUN: mlir-opt %s -one-shot-bufferize="allow-return-allocs bufferize-function-boundaries" -drop-equivalent-buffer-results -split-input-file | FileCheck %s
2
3// Run fuzzer with different seeds.
4// RUN: mlir-opt %s -one-shot-bufferize="allow-return-allocs test-analysis-only analysis-fuzzer-seed=23 bufferize-function-boundaries" -split-input-file -o /dev/null
5// RUN: mlir-opt %s -one-shot-bufferize="allow-return-allocs test-analysis-only analysis-fuzzer-seed=59 bufferize-function-boundaries" -split-input-file -o /dev/null
6// RUN: mlir-opt %s -one-shot-bufferize="allow-return-allocs test-analysis-only analysis-fuzzer-seed=91 bufferize-function-boundaries" -split-input-file -o /dev/null
7
8// Test bufferization using memref types that have no layout map.
9// RUN: mlir-opt %s -one-shot-bufferize="allow-return-allocs unknown-type-conversion=identity-layout-map bufferize-function-boundaries" -split-input-file -o /dev/null
10
11// CHECK-DAG: #[[$map_1d_dyn:.*]] = affine_map<(d0)[s0, s1] -> (d0 * s1 + s0)>
12
13// CHECK-LABEL: func @insert_slice_fun
14//  CHECK-SAME:   %[[A0:[a-zA-Z0-9]*]]: memref<?xf32, #[[$map_1d_dyn]]>,
15//  CHECK-SAME:   %[[A1:[a-zA-Z0-9]*]]: memref<?xf32, #[[$map_1d_dyn]]>,
16//  CHECK-SAME:   %[[t0:[a-zA-Z0-9]*]]: memref<4xf32, #[[$map_1d_dyn]]>,
17//  CHECK-SAME:   %[[t1:[a-zA-Z0-9]*]]: memref<4xf32, #[[$map_1d_dyn]]>
18func.func @insert_slice_fun(
19    %A0 : tensor<?xf32> {bufferization.writable = false},
20    %A1 : tensor<?xf32> {bufferization.writable = true},
21    %t0 : tensor<4xf32> {bufferization.writable = false},
22    %t1 : tensor<4xf32> {bufferization.writable = true})
23  ->  (tensor<?xf32>, tensor<?xf32>, tensor<?xf32>, tensor<?xf32>)
24{
25  // Hoisted allocs.
26  //      CHECK: %[[REALLOC1:.*]] = memref.alloc
27  //      CHECK: %[[REALLOC2:.*]] = memref.alloc
28  //      CHECK: %[[REALLOC3:.*]] = memref.alloc
29
30  // Alloc and copy the whole result tensor. Copy the tensor.extract_slice.
31  //      CHECK: memref.copy %[[A0]], %[[REALLOC3]]
32  //      CHECK: %[[SV_A0:.*]] = memref.subview %[[REALLOC3]]
33  //      CHECK: memref.copy %[[t0]], %[[SV_A0]]
34  %r0 = tensor.insert_slice %t0 into %A0[0][4][1] : tensor<4xf32> into tensor<?xf32>
35
36  // Alloc and copy the whole result tensor. Copy the tensor.extract_slice.
37  //      CHECK: memref.copy %[[A0]]
38  //      CHECK: %[[SV_A0_2:.*]] = memref.subview %[[REALLOC2]]
39  //      CHECK: memref.copy %[[t1]], %[[SV_A0_2]]
40  %r1 = tensor.insert_slice %t1 into %A0[0][4][1] : tensor<4xf32> into tensor<?xf32>
41
42  //  Still alloc the large tensor because %A1 is read after. Copy the tensor.extract_slice.
43  //      CHECK: memref.copy %[[A1]]
44  //      CHECK: %[[SV_A1:.*]] = memref.subview %[[REALLOC1]]
45  //      CHECK: memref.copy %[[t0]], %[[SV_A1]]
46  %r2 = tensor.insert_slice %t0 into %A1[0][4][1] : tensor<4xf32> into tensor<?xf32>
47
48  //  Do not realloc the large tensor. Copy the tensor.extract_slice.
49  //  CHECK-NOT: alloc
50  //      CHECK: %[[SV_A1_2:.*]] = memref.subview %[[A1]]
51  //      CHECK: memref.copy %[[t1]], %[[SV_A1_2]]
52  %r3 = tensor.insert_slice %t1 into %A1[0][4][1] : tensor<4xf32> into tensor<?xf32>
53
54  //      CHECK: return %[[REALLOC3]], %[[REALLOC2]], %[[REALLOC1]] :
55  // CHECK-SAME:   memref<?xf32>, memref<?xf32>, memref<?xf32>
56  return %r0, %r1, %r2, %r3: tensor<?xf32>, tensor<?xf32>, tensor<?xf32>, tensor<?xf32>
57}
58
59// -----
60
61// CHECK-DAG: #[[$map_1d_dyn:.*]] = affine_map<(d0)[s0, s1] -> (d0 * s1 + s0)>
62
63// CHECK-LABEL: func @insert_slice_fun
64//  CHECK-SAME:   %[[A:[a-zA-Z0-9]*]]: memref<?xf32, #[[$map_1d_dyn]]>
65//  CHECK-SAME:   %[[t:[a-zA-Z0-9]*]]: memref<4xf32, #[[$map_1d_dyn]]>
66func.func @insert_slice_fun(
67    %A : tensor<?xf32> {bufferization.writable = true},
68    %t : tensor<4xf32> {bufferization.writable = false})
69  -> tensor<?xf32>
70{
71  %f0 = arith.constant 0.0 : f32
72
73  //  CHECK-NOT: alloc
74  //      CHECK: %[[SV_A:.*]] = memref.subview %[[A]]
75  //      CHECK: memref.copy %[[t]], %[[SV_A]]
76  %r0 = tensor.insert_slice %t into %A[0][4][1] : tensor<4xf32> into tensor<?xf32>
77
78  /// Overwrite A inplace.
79  //      CHECK: linalg.fill ins({{.*}}{{.*}}outs(%[[A]]
80  %r1 = linalg.fill ins(%f0 : f32) outs(%r0 : tensor<?xf32>) -> tensor<?xf32>
81
82  //     CHECK: return
83  // CHECK-NOT: tensor
84  return %r1: tensor<?xf32>
85}
86
87// -----
88
89// CHECK-DAG: #[[$map_1d_dyn:.*]] = affine_map<(d0)[s0, s1] -> (d0 * s1 + s0)>
90
91// CHECK-LABEL: func @insert_slice_fun
92//  CHECK-SAME:   %[[A:[a-zA-Z0-9]*]]: memref<?xf32, #[[$map_1d_dyn]]>
93//  CHECK-SAME:   %[[t:[a-zA-Z0-9]*]]: memref<4xf32, #[[$map_1d_dyn]]>
94func.func @insert_slice_fun(
95    %A : tensor<?xf32> {bufferization.writable = true},
96    %t : tensor<4xf32> {bufferization.writable = false})
97  -> tensor<?xf32>
98{
99  %f0 = arith.constant 0.0 : f32
100
101  //      CHECK: linalg.fill ins({{.*}}{{.*}}outs(%[[A]]
102  %r0 = linalg.fill ins(%f0 : f32) outs(%A : tensor<?xf32>) -> tensor<?xf32>
103
104  //  CHECK-NOT: alloc
105  //      CHECK: %[[SV_A:.*]] = memref.subview %[[A]]
106  /// Overwrite A inplace by copying into the subview.
107  //      CHECK: memref.copy %[[t]], %[[SV_A]]
108  %r1 = tensor.insert_slice %t into %r0[0][4][1] : tensor<4xf32> into tensor<?xf32>
109
110  //     CHECK: return
111  // CHECK-NOT: tensor
112  return %r1: tensor<?xf32>
113}
114
115// -----
116
117// CHECK-DAG: #[[$map_1d_dyn:.*]] = affine_map<(d0)[s0, s1] -> (d0 * s1 + s0)>
118
119// CHECK-LABEL: func @insert_slice_fun_not_inplace
120//  CHECK-SAME:   %[[A:[a-zA-Z0-9]*]]: memref<?xf32, #[[$map_1d_dyn]]>
121//  CHECK-SAME:   %[[t:[a-zA-Z0-9]*]]: memref<4xf32, #[[$map_1d_dyn]]>
122func.func @insert_slice_fun_not_inplace(
123    %A : tensor<?xf32> {bufferization.writable = false},
124    %t : tensor<4xf32> {bufferization.writable = false})
125  -> tensor<?xf32>
126{
127  //      CHECK: %[[ALLOC:.*]] = memref.alloc(%{{.*}}) {alignment = 128 : i64} : memref<?xf32>
128  //      CHECK: memref.copy %[[A]], %[[ALLOC]] : memref<?xf32{{.*}} to memref<?xf32>
129  //      CHECK: %[[SV:.*]] = memref.subview %[[ALLOC]][0] [4] [1] : memref<?xf32> to memref<4xf32>
130  //      CHECK: memref.copy %[[t]], %[[SV]] : memref<4xf32, #map> to memref<4xf32>
131  %r0 = tensor.insert_slice %t into %A[0][4][1] : tensor<4xf32> into tensor<?xf32>
132
133  //     CHECK: return %{{.*}} : memref<?xf32>
134  return %r0: tensor<?xf32>
135}
136
137// -----
138
139// CHECK-LABEL: func @tensor_cast_not_in_place(
140//  CHECK-SAME:     %[[A:.*]]: memref<?xf32{{.*}}>, %[[B:.*]]: memref<?xf32{{.*}}>
141//       CHECK:   %[[alloc:.*]] = memref.alloc
142//       CHECK:   memref.copy %[[A]], %[[alloc]]
143//       CHECK:   %[[subview:.*]] = memref.subview %[[A]][{{.*}}] [4] [1] : {{.*}} to memref<4xf32
144//       CHECK:   memref.copy %[[alloc]], %[[subview]]
145func.func @tensor_cast_not_in_place(
146    %A : tensor<?xf32> {bufferization.writable = true},
147    %B : tensor<?xf32> {bufferization.writable = false}, %idx: index)
148  -> (tensor<?xf32>)
149{
150  %r0 = tensor.cast %A : tensor<?xf32> to tensor<4xf32>
151  %r1 = tensor.insert_slice %r0 into %A[%idx][4][1] : tensor<4xf32> into tensor<?xf32>
152  return %r1 : tensor<?xf32>
153}
154
155// -----
156
157// CHECK-LABEL: func @insert_op
158//  CHECK-SAME:     %[[t1:.*]]: memref<?xf32, {{.*}}>, %[[s:.*]]: f32, %[[i:.*]]: index
159func.func @insert_op(%t1 : tensor<?xf32> {bufferization.writable = true},
160                     %s : f32, %i : index) -> tensor<?xf32> {
161  // CHECK: memref.store %[[s]], %[[t1]][%[[i]]]
162  %0 = tensor.insert %s into %t1[%i] : tensor<?xf32>
163  // CHECK: return
164  return %0 : tensor<?xf32>
165}
166
167// -----
168
169// A regression test to make sure that we handle rank-reducing extract_slice
170// correctly.
171
172// CHECK-LABEL: func @rank_reducing
173func.func @rank_reducing(
174    %i: index, %j: index,
175    %arg0: tensor<8x18x32xf32>)
176      -> tensor<?x1x6x8xf32> {
177  %c1 = arith.constant 1 : index
178  %c6 = arith.constant 6 : index
179  %c8 = arith.constant 8 : index
180  %c32 = arith.constant 32 : index
181  %c0 = arith.constant 0 : index
182  %0 = bufferization.alloc_tensor() : tensor<4x1x6x8xf32>
183  %1 = tensor.cast %0 : tensor<4x1x6x8xf32> to tensor<?x1x6x8xf32>
184  %2 = bufferization.alloc_tensor() : tensor<1x6x8xf32>
185  %5 = scf.for %arg7 = %c0 to %c32 step %c8 iter_args(%arg8 = %1) -> (tensor<?x1x6x8xf32>) {
186    %7 = affine.apply affine_map<(d0) -> (d0 ceildiv 8)>(%arg7)
187    %8 = tensor.extract_slice %arg0[%i, %j, %arg7] [1, 6, 8] [1, 1, 1] : tensor<8x18x32xf32> to tensor<1x6x8xf32>
188    %9 = scf.for %arg9 = %c0 to %c6 step %c1 iter_args(%arg10 = %2) -> (tensor<1x6x8xf32>) {
189      %11 = tensor.extract_slice %8[0, %arg9, 0] [1, 1, 8] [1, 1, 1] : tensor<1x6x8xf32> to tensor<1x1x8xf32>
190      %12 = tensor.insert_slice %11 into %arg10[0, %arg9, 0] [1, 1, 8] [1, 1, 1] : tensor<1x1x8xf32> into tensor<1x6x8xf32>
191      scf.yield %12 : tensor<1x6x8xf32>
192    }
193    %10 = tensor.insert_slice %9 into %arg8[%7, 0, 0, 0] [1, 1, 6, 8] [1, 1, 1, 1] : tensor<1x6x8xf32> into tensor<?x1x6x8xf32>
194    scf.yield %10 : tensor<?x1x6x8xf32>
195  }
196  return %5: tensor<?x1x6x8xf32>
197}
198