1// RUN: mlir-opt -linalg-bufferize  -canonicalize -cse -split-input-file %s | FileCheck %s
2
3#map0 = affine_map<(d0) -> (d0)>
4
5// In-depth checking of a basic case, this is testing
6// - memref.buffer_cast / memref.tensor_load materializations are properly inserted
7// - payload is correctly carried over
8// - affine maps are correctly carried over
9// Later tests will not check all these details.
10
11// CHECK: #map = affine_map<(d0) -> (d0)>
12// CHECK-LABEL:   func @basic(
13// CHECK-SAME:                %[[TENSOR:.*]]: tensor<4xf32>) -> tensor<4xf32> {
14// CHECK:           %[[MEMREF:.*]] = memref.buffer_cast %[[TENSOR]] : memref<4xf32>
15// CHECK:           %[[RESULT_MEMREF:.*]] = memref.alloc() : memref<4xf32>
16// CHECK:           linalg.generic {indexing_maps = [#map, #map], iterator_types = ["parallel"]}
17// CHECK-SAME:      ins(%[[MEMREF]] : memref<4xf32>)
18// CHECK-SAME:      outs(%[[RESULT_MEMREF]] : memref<4xf32>) {
19// CHECK:           ^bb0(%[[RESULT1:.*]]: f32, %[[UNUSED:.*]]: f32):
20// CHECK:             %[[DIM1:.*]] = math.exp %[[RESULT1]] : f32
21// CHECK:             linalg.yield %[[DIM1]] : f32
22// CHECK:           }
23// CHECK:           %[[RESULT:.*]] = memref.tensor_load %[[RESULT_MEMREF]] : memref<4xf32>
24// CHECK:           return %[[RESULT]] : tensor<4xf32>
25func @basic(%arg0: tensor<4xf32>) -> tensor<4xf32> {
26    %0 = linalg.generic {
27      indexing_maps = [#map0, #map0],
28      iterator_types = ["parallel"]
29    } ins(%arg0 : tensor<4xf32>)
30      outs(%arg0 : tensor<4xf32>) {
31      ^bb0(%gen_arg1: f32, %out: f32):
32        %tmp1 = math.exp %gen_arg1 : f32
33        linalg.yield %tmp1 : f32
34    } -> tensor<4xf32>
35    return %0 : tensor<4xf32>
36}
37
38
39// -----
40
41#map0 = affine_map<(d0) -> (d0)>
42
43// Same as above but with linalg.init_tensor op.
44
45// CHECK: #map = affine_map<(d0) -> (d0)>
46// CHECK-LABEL: func @init_tensor(
47// CHECK-SAME:      %[[IN:.*]]: tensor<?xf32>, %[[SIZE:.*]]: index)
48// CHECK:         %[[MEMREF:.*]] = memref.buffer_cast %[[IN]] : memref<?xf32>
49// CHECK:         %[[OUT_BUF:.*]] = memref.alloc(%[[SIZE]]) : memref<?xf32>
50// CHECK:         linalg.generic
51// CHECK-SAME:    ins(%[[MEMREF]] : memref<?xf32>)
52// CHECK-SAME:    outs(%[[OUT_BUF]] : memref<?xf32>) {
53func @init_tensor(%in : tensor<?xf32>, %size: index) -> tensor<?xf32> {
54  %init = linalg.init_tensor [%size] : tensor<?xf32>
55  %0 = linalg.generic {
56    indexing_maps = [#map0, #map0],
57    iterator_types = ["parallel"]
58  } ins(%in : tensor<?xf32>)
59    outs(%init : tensor<?xf32>) {
60    ^bb0(%gen_arg1: f32, %out: f32):
61      %tmp1 = math.exp %gen_arg1 : f32
62      linalg.yield %tmp1 : f32
63  } -> tensor<?xf32>
64  return %0 : tensor<?xf32>
65}
66
67
68// -----
69
70#map0 = affine_map<(d0) -> (d0)>
71
72// CHECK-LABEL:   func @multiple_results
73// CHECK:           %[[RESULT0:.*]] = memref.alloc() : memref<4xf32>
74// CHECK:           %[[RESULT1:.*]] = memref.alloc() : memref<4xf32>
75// CHECK:           linalg.generic
76// CHECK-SAME:      ins(%{{.*}} : memref<4xf32>)
77// CHECK-SAME:      outs(%[[RESULT0]], %[[RESULT1]] : memref<4xf32>, memref<4xf32>)
78// CHECK-NEXT: ^bb0(%{{.*}}: f32, %{{.*}}: f32, %{{.*}}: f32):
79func @multiple_results(%arg0: tensor<4xf32>) -> (tensor<4xf32>, tensor<4xf32>) {
80    %0, %1 = linalg.generic {
81      indexing_maps = [#map0, #map0, #map0],
82      iterator_types = ["parallel"]
83    } ins(%arg0 : tensor<4xf32>)
84      outs (%arg0, %arg0 : tensor<4xf32>, tensor<4xf32>) {
85      ^bb0(%gen_arg1: f32, %out1: f32, %out2: f32):
86        %tmp1 = math.exp %gen_arg1 : f32
87        linalg.yield %tmp1, %tmp1 : f32, f32
88    } -> (tensor<4xf32>, tensor<4xf32>)
89    return %0, %1 : tensor<4xf32>, tensor<4xf32>
90}
91
92// -----
93
94#map_2d = affine_map<(d0, d1) -> (d0, d1)>
95
96// Check that the allocs properly consider the different shapes of the output
97// operands. The permuted indexing maps translate to different output shapes.
98
99// CHECK-LABEL:   func @dynamic_results(
100// CHECK-SAME:                          %[[ARG:.*]]: tensor<?x?xf32>
101// CHECK-DAG:       %[[C0:.*]] = constant 0 : index
102// CHECK-DAG:       %[[C1:.*]] = constant 1 : index
103// CHECK:           %[[MEMREF_ARG:.*]] = memref.buffer_cast %[[ARG]] : memref<?x?xf32>
104// CHECK:           %[[DIM0:.*]] = tensor.dim %[[ARG]], %[[C0]] : tensor<?x?xf32>
105// CHECK:           %[[DIM1:.*]] = tensor.dim %[[ARG]], %[[C1]] : tensor<?x?xf32>
106// CHECK:           %[[RESULT0:.*]] = memref.alloc(%[[DIM0]], %[[DIM1]]) : memref<?x?xf32>
107// CHECK:           %[[RESULT1:.*]] = memref.alloc(%[[DIM0]], %[[DIM1]]) : memref<?x?xf32>
108// CHECK:           linalg.generic
109// CHECK-SAME:      ins(%[[MEMREF_ARG]] : memref<?x?xf32>)
110// CHECK-SAME:      outs(%[[RESULT0]], %[[RESULT1]] : memref<?x?xf32>, memref<?x?xf32>)
111func @dynamic_results(%arg0: tensor<?x?xf32>)
112         -> (tensor<?x?xf32>, tensor<?x?xf32>) {
113    %0, %1 = linalg.generic {
114      indexing_maps = [#map_2d, #map_2d, #map_2d],
115      iterator_types = ["parallel", "parallel"]
116    } ins(%arg0 : tensor<?x?xf32>)
117      outs (%arg0, %arg0 : tensor<?x?xf32>, tensor<?x?xf32>) {
118      ^bb0(%gen_arg1: f32, %out1: f32, %out2: f32):
119        %tmp1 = math.exp %gen_arg1 : f32
120        linalg.yield %tmp1, %tmp1 : f32, f32
121    } -> (tensor<?x?xf32>, tensor<?x?xf32>)
122    return %0, %1 : tensor<?x?xf32>, tensor<?x?xf32>
123}
124
125// -----
126
127#accesses = [
128  affine_map<(i, j, k) -> (j, i, k)>,
129  affine_map<(i, j, k) -> (i, j)>
130]
131
132#trait = {
133  indexing_maps = #accesses,
134  iterator_types = ["parallel", "parallel", "reduction"]
135}
136
137// Check the bufferization of init tensors.
138
139// CHECK-LABEL:   func @generic_with_init_tensor(
140// CHECK-SAME:                                   %[[ARG0_TENSOR:.*]]: tensor<2x3x4xvector<3x4xi4>>,
141// CHECK-SAME:                                   %[[ARG1_TENSOR:.*]]: tensor<3x2xf32>) -> tensor<3x2xf32> {
142// CHECK:           %[[ARG0_MEMREF:.*]] = memref.buffer_cast %[[ARG0_TENSOR]] : memref<2x3x4xvector<3x4xi4>>
143// CHECK:           %[[ARG1_MEMREF:.*]] = memref.buffer_cast %[[ARG1_TENSOR]] : memref<3x2xf32>
144// CHECK:           %[[INIT_BUFFER:.*]] = memref.alloc() : memref<3x2xf32>
145// CHECK:           linalg.copy(%[[ARG1_MEMREF]], %[[INIT_BUFFER]]) : memref<3x2xf32>, memref<3x2xf32>
146// CHECK:           linalg.generic
147// CHECK-SAME:      ins(%[[ARG0_MEMREF]] : memref<2x3x4xvector<3x4xi4>>)
148// CHECK-SAME:      outs(%[[INIT_BUFFER]] : memref<3x2xf32>) {
149func @generic_with_init_tensor(%arg0: tensor<2x3x4xvector<3x4xi4>>,
150  %arg1: tensor<3x2xf32>) -> (tensor<3x2xf32>) {
151
152  %0 = linalg.generic #trait
153    ins(%arg0 : tensor<2x3x4xvector<3x4xi4>>)
154   outs(%arg1 : tensor<3x2xf32>) {
155    ^bb(%v0: vector<3x4xi4>, %v1: f32) :
156      linalg.yield %v1 : f32
157  } -> tensor<3x2xf32>
158
159  return %0 : tensor<3x2xf32>
160}
161
162// -----
163
164// CHECK-DAG: #[[$MAP0:[0-9a-z]*]] = affine_map<(d0, d1)[s0, s1] -> (d0 * s1 + s0 + d1)>
165// CHECK-DAG: #[[$MAP1:[0-9a-z]*]] = affine_map<(d0, d1)[s0, s1] -> (d0 * s1 + s0 + d1 * 2)>
166
167func private @make_index() -> index
168
169// CHECK-LABEL: func @bufferize_slice(
170//  CHECK-SAME:   %[[T:[0-9a-z]*]]: tensor<?x?xf32>
171func @bufferize_slice(%t : tensor<?x?xf32>) -> (tensor<2x3xf32>, tensor<2x?xf32>) {
172  //      CHECK: %[[IDX:.*]] = call @make_index() : () -> index
173  %i0 = call @make_index() : () -> index
174
175  //      CHECK: %[[M:.*]] = memref.buffer_cast %[[T]] : memref<?x?xf32>
176  // CHECK-NEXT: %[[A0:.*]] = memref.alloc() : memref<2x3xf32>
177  // CHECK-NEXT: %[[SM0:.*]] = memref.subview %[[M]][0, 0] [2, 3] [1, 1]
178  // CHECK-SAME:   memref<?x?xf32> to memref<2x3xf32, #[[$MAP0]]>
179  // CHECK-NEXT: linalg.copy(%[[SM0]], %[[A0]]) : memref<2x3xf32, #[[$MAP0]]>, memref<2x3xf32>
180  // CHECK-NEXT: %[[RT0:.*]] = memref.tensor_load %[[A0]] : memref<2x3xf32>
181  %st0 = tensor.extract_slice %t[0, 0][2, 3][1, 1] : tensor<?x?xf32> to tensor<2x3xf32>
182
183  // CHECK-NEXT: %[[A1:.*]] = memref.alloc(%[[IDX]]) : memref<2x?xf32>
184  // CHECK-NEXT: %[[SM1:.*]] = memref.subview %[[M]][0, %[[IDX]]] [2, %[[IDX]]] [1, 2]
185  // CHECK-SAME:   memref<?x?xf32> to memref<2x?xf32, #[[$MAP1]]>
186  // CHECK-NEXT: linalg.copy(%[[SM1]], %[[A1]]) : memref<2x?xf32, #[[$MAP1]]>, memref<2x?xf32>
187  // CHECK-NEXT: %[[RT1:.*]] = memref.tensor_load %[[A1]] : memref<2x?xf32>
188  %st1 = tensor.extract_slice %t[0, %i0][2, %i0][1, 2] : tensor<?x?xf32> to tensor<2x?xf32>
189
190  // CHECK-NEXT: return %[[RT0]], %[[RT1]]
191  return %st0, %st1 : tensor<2x3xf32>, tensor<2x?xf32>
192}
193
194// -----
195
196// CHECK-DAG: #[[$MAP0:[0-9a-z]*]] = affine_map<(d0, d1)[s0, s1] -> (d0 * s1 + s0 + d1)>
197// CHECK-DAG: #[[$MAP1:[0-9a-z]*]] = affine_map<(d0, d1)[s0, s1] -> (d0 * s1 + s0 + d1 * 2)>
198
199func private @make_index() -> index
200
201// CHECK-LABEL: func @bufferize_insert_slice(
202//  CHECK-SAME:   %[[T:[0-9a-z]*]]: tensor<?x?xf32>
203//  CHECK-SAME:   %[[ST0:[0-9a-z]*]]: tensor<2x3xf32>
204//  CHECK-SAME:   %[[ST1:[0-9a-z]*]]: tensor<2x?xf32>
205func @bufferize_insert_slice(%t : tensor<?x?xf32>, %st0 : tensor<2x3xf32>, %st1 : tensor<2x?xf32>) ->
206    (tensor<?x?xf32>, tensor<?x?xf32>) {
207  %c0 = constant 0 : index
208  %c1 = constant 1 : index
209  // CHECK-DAG: %[[C0:.*]] = constant 0 : index
210  // CHECK-DAG: %[[C1:.*]] = constant 1 : index
211  %i0 = call @make_index() : () -> index
212  // CHECK: %[[IDX:.*]] = call @make_index() : () -> index
213
214
215  // CHECK-DAG: %[[M:.*]] = memref.buffer_cast %[[T]] : memref<?x?xf32>
216  // CHECK-DAG: %[[SM0:.*]] = memref.buffer_cast %[[ST0]] : memref<2x3xf32>
217  // CHECK-NEXT: %[[DIM0:.*]] = tensor.dim %[[T]], %[[C0]] : tensor<?x?xf32>
218  // CHECK-NEXT: %[[DIM1:.*]] = tensor.dim %[[T]], %[[C1]] : tensor<?x?xf32>
219  // CHECK-NEXT: %[[M_COPY0:.*]] = memref.alloc(%[[DIM0]], %[[DIM1]]) : memref<?x?xf32>
220  // CHECK-NEXT: linalg.copy(%[[M]], %[[M_COPY0]]) : memref<?x?xf32>, memref<?x?xf32>
221  // CHECK-NEXT: %[[SUBVIEW0:.*]] = memref.subview %[[M_COPY0]][0, 0] [2, 3] [1, 1]
222  // CHECK-SAME:   memref<?x?xf32> to memref<2x3xf32, #[[$MAP0]]>
223  // CHECK-NEXT: linalg.copy(%[[SM0]], %[[SUBVIEW0]]) : memref<2x3xf32>, memref<2x3xf32, #[[$MAP0]]>
224  // CHECK-NEXT: %[[RT0:.*]] = memref.tensor_load %[[M_COPY0]] : memref<?x?xf32>
225  %t0 = tensor.insert_slice %st0 into %t[0, 0][2, 3][1, 1] : tensor<2x3xf32> into tensor<?x?xf32>
226
227  //  CHECK-DAG: %[[SM1:.*]] = memref.buffer_cast %[[ST1]] : memref<2x?xf32>
228  // CHECK-NEXT: %[[M_COPY1:.*]] = memref.alloc(%[[DIM0]], %[[DIM1]]) : memref<?x?xf32>
229  // CHECK-NEXT: linalg.copy(%[[M]], %[[M_COPY1]]) : memref<?x?xf32>, memref<?x?xf32>
230  // CHECK-NEXT: %[[SUBVIEW1:.*]] = memref.subview %[[M_COPY1]][0, %[[IDX]]] [2, %[[IDX]]] [1, 2]
231  // CHECK-SAME:   memref<?x?xf32> to memref<2x?xf32, #[[$MAP1]]>
232  // CHECK-NEXT: linalg.copy(%[[SM1]], %[[SUBVIEW1]]) : memref<2x?xf32>, memref<2x?xf32, #[[$MAP1]]>
233  // CHECK-NEXT: %[[RT1:.*]] = memref.tensor_load %[[M_COPY1]] : memref<?x?xf32>
234  %t1 = tensor.insert_slice %st1 into %t[0, %i0][2, %i0][1, 2] : tensor<2x?xf32> into tensor<?x?xf32>
235
236  //     CHECK: return %[[RT0]], %[[RT1]]
237  return %t0, %t1: tensor<?x?xf32>, tensor<?x?xf32>
238}
239
240// -----
241
242// CHECK-LABEL: func @bufferize_fill(
243// CHECK-SAME:    %[[IN:.*]]: tensor<?xf32>
244func @bufferize_fill(%arg0: tensor<?xf32>) -> tensor<?xf32> {
245  %c0 = constant 0.0 : f32
246  // CHECK: %[[MEMREF:.*]] = memref.buffer_cast %[[IN]] : memref<?xf32>
247  // CHECK: linalg.fill(%cst, %[[MEMREF]]) : f32, memref<?xf32>
248  // CHECK: %[[TENSOR:.*]] = memref.tensor_load %[[MEMREF]] : memref<?xf32>
249  // CHECK: return %[[TENSOR]]
250  %0 = linalg.fill(%c0, %arg0) : f32, tensor<?xf32> -> tensor<?xf32>
251  return %0 : tensor<?xf32>
252}
253
254// -----
255
256// CHECK-LABEL: func @bufferize_tensor_collapse_shape(
257// CHECK-SAME:    %[[IN:.*]]: tensor<4x5xf32>
258func @bufferize_tensor_collapse_shape(%arg0: tensor<4x5xf32>) -> tensor<20xf32> {
259  %out = linalg.tensor_collapse_shape %arg0 [[0, 1]] :
260     tensor<4x5xf32> into tensor<20xf32>
261  return %out : tensor<20xf32>
262}
263// CHECK: %[[MEMREF:.*]] = memref.buffer_cast %[[IN]] : memref<4x5xf32>
264// CHECK: %[[RESHAPE:.*]] = memref.collapse_shape %[[MEMREF]] {{\[}}[0, 1]]
265// CHECK-SAME: : memref<4x5xf32> into memref<20xf32>
266// CHECK: %[[TENSOR:.*]] = memref.tensor_load %[[RESHAPE]] : memref<20xf32>
267// CHECK: return %[[TENSOR]]
268
269// -----
270
271// CHECK-LABEL:   func @pad_tensor_dynamic_shape(
272// CHECK-SAME:                                   %[[IN:.*]]: tensor<4x?x2x?xf32>,
273// CHECK-SAME:                                   %[[OFFSET:.*]]: index) -> tensor<4x?x?x?xf32> {
274func @pad_tensor_dynamic_shape(%arg0: tensor<4x?x2x?xf32>, %arg1: index) -> tensor<4x?x?x?xf32> {
275  %c0 = constant 0 : index
276  %cst = constant 0.0 : f32
277  %out = linalg.pad_tensor %arg0 low[%c0, %c0, %arg1, %c0] high[%c0, %c0, %c0, %arg1]  {
278  ^bb0(%gen_arg1: index, %gen_arg2: index, %gen_arg3: index, %gen_arg4: index):  // no predecessors
279    linalg.yield %cst : f32
280  } : tensor<4x?x2x?xf32> to tensor<4x?x?x?xf32>
281  return %out : tensor<4x?x?x?xf32>
282}
283
284// CHECK-DAG:       %[[C3:.*]] = constant 3 : index
285// CHECK-DAG:       %[[C2:.*]] = constant 2 : index
286// CHECK-DAG:       %[[C1:.*]] = constant 1 : index
287// CHECK-DAG:       %[[CST:.*]] = constant 0.000000e+00 : f32
288// CHECK:           %[[DIM1:.*]] = tensor.dim %[[IN]], %[[C1]] : tensor<4x?x2x?xf32>
289// CHECK:           %[[OUT_DIM2:.*]] = addi %[[OFFSET]], %[[C2]] : index
290// CHECK:           %[[DIM3:.*]] = tensor.dim %[[IN]], %[[C3]] : tensor<4x?x2x?xf32>
291// CHECK:           %[[OUT_DIM3:.*]] = addi %[[DIM3]], %[[OFFSET]] : index
292// CHECK:           %[[FILLED:.*]] = memref.alloc(%[[DIM1]], %[[OUT_DIM2]], %[[OUT_DIM3]]) : memref<4x?x?x?xf32>
293// CHECK:           linalg.fill(%[[CST]], %[[FILLED]]) : f32, memref<4x?x?x?xf32>
294// CHECK:           %[[IN_MEMREF:.*]] = memref.buffer_cast %[[IN]] : memref<4x?x2x?xf32>
295// CHECK:           %[[OUT:.*]] = memref.alloc(%[[DIM1]], %[[OUT_DIM2]], %[[OUT_DIM3]]) : memref<4x?x?x?xf32>
296// CHECK:           linalg.copy(%[[FILLED]], %[[OUT]]) : memref<4x?x?x?xf32>, memref<4x?x?x?xf32>
297// CHECK:           %[[INTERIOR:.*]] = memref.subview %[[OUT]][0, 0, %[[OFFSET]], 0] [4, %[[DIM1]], 2, %[[DIM3]]] [1, 1, 1, 1] : memref<4x?x?x?xf32> to memref<4x?x2x?xf32, #map>
298// CHECK:           linalg.copy(%[[IN_MEMREF]], %[[INTERIOR]]) : memref<4x?x2x?xf32>, memref<4x?x2x?xf32, #map>
299// CHECK:           %[[OUT_TENSOR:.*]] = memref.tensor_load %[[OUT]] : memref<4x?x?x?xf32>
300// CHECK:           return %[[OUT_TENSOR]] : tensor<4x?x?x?xf32>
301// CHECK:         }
302
303
304// -----
305
306// CHECK-LABEL:   func @vector_transfer
307func @vector_transfer(%in: tensor<4xf32>, %out: tensor<4xf32>) {
308  %c0 = constant 0 : index
309  %cst = constant 0.000000e+00 : f32
310  %read = vector.transfer_read %in[%c0], %cst {in_bounds = [true]}
311      : tensor<4xf32>, vector<4xf32>
312  %tanh = math.tanh %read : vector<4xf32>
313  %write = vector.transfer_write %tanh, %out[%c0] {in_bounds = [true]}
314      : vector<4xf32>, tensor<4xf32>
315  return
316  // CHECK: vector.transfer_read {{.*}} : memref<4xf32>, vector<4xf32>
317  // CHECK: vector.transfer_write {{.*}} : vector<4xf32>, memref<4xf32>
318}
319