1// RUN: mlir-opt %s -allow-unregistered-dialect -one-shot-bufferize="allow-return-allocs allow-unknown-ops" -split-input-file | FileCheck %s
2
3// Test bufferization using memref types that have no layout map.
4// RUN: mlir-opt %s -allow-unregistered-dialect -one-shot-bufferize="allow-return-allocs allow-unknown-ops unknown-type-conversion=identity-layout-map" -split-input-file | FileCheck %s --check-prefix=CHECK-NO-LAYOUT-MAP
5
6// Run fuzzer with different seeds.
7// RUN: mlir-opt %s -allow-unregistered-dialect -one-shot-bufferize="allow-return-allocs test-analysis-only analysis-fuzzer-seed=23" -split-input-file -o /dev/null
8// RUN: mlir-opt %s -allow-unregistered-dialect -one-shot-bufferize="allow-return-allocs test-analysis-only analysis-fuzzer-seed=59" -split-input-file -o /dev/null
9// RUN: mlir-opt %s -allow-unregistered-dialect -one-shot-bufferize="allow-return-allocs test-analysis-only analysis-fuzzer-seed=91" -split-input-file -o /dev/null
10
11// RUN: mlir-opt %s -allow-unregistered-dialect -one-shot-bufferize="dialect-filter=tensor,bufferization allow-unknown-ops allow-return-allocs" -canonicalize -split-input-file | FileCheck %s --check-prefix=CHECK-TENSOR
12// RUN: mlir-opt %s -allow-unregistered-dialect -one-shot-bufferize="dialect-filter=scf,bufferization allow-unknown-ops allow-return-allocs" -canonicalize -split-input-file | FileCheck %s --check-prefix=CHECK-SCF
13
14// CHECK: #[[$MAP:.*]] = affine_map<(d0)[s0, s1] -> (d0 * s1 + s0)>
15
16// CHECK-LABEL: func @use_of_unknown_op_1(
17//  CHECK-SAME:     %[[t1:.*]]: tensor<?xf32>
18// CHECK-NO-LAYOUT-MAP-LABEL: func @use_of_unknown_op_1(
19//  CHECK-NO-LAYOUT-MAP-SAME:     %[[t1:.*]]: tensor<?xf32>
20func.func @use_of_unknown_op_1(%t1: tensor<?xf32>)
21    -> vector<5xf32> {
22  // ToTensorOp is generated because the function is bufferized and has a
23  // memref block argument.
24  // CHECK: %[[dummy:.*]] = "test.dummy_op"(%[[t1]])
25  // CHECK-NO-LAYOUT-MAP: %[[dummy:.*]] = "test.dummy_op"(%[[t1]])
26  %0 = "test.dummy_op"(%t1) : (tensor<?xf32>) -> tensor<?xf32>
27
28  %idx = arith.constant 0 : index
29  %cst = arith.constant 0.0 : f32
30  // CHECK: %[[dummy_memref:.*]] = bufferization.to_memref %[[dummy]] : memref<?xf32, #[[$MAP]]>
31  // CHECK: vector.transfer_read %[[dummy_memref]][%{{.*}}], %{{.*}} : memref<?xf32, #[[$MAP]]>
32  // CHECK-NO-LAYOUT-MAP: %[[dummy_memref:.*]] = bufferization.to_memref %[[dummy]] : memref<?xf32>
33  // CHECK-NO-LAYOUT-MAP: vector.transfer_read %[[dummy_memref]][%{{.*}}], %{{.*}} : memref<?xf32>
34  %1 = vector.transfer_read %0[%idx], %cst : tensor<?xf32>, vector<5xf32>
35  return %1 : vector<5xf32>
36}
37
38// -----
39
40// CHECK-LABEL: func @use_of_unknown_op_2(
41//  CHECK-SAME:     %[[t1:.*]]: tensor<?xf32>
42func.func @use_of_unknown_op_2(%t1: tensor<?xf32>) -> tensor<?xf32> {
43  // CHECK: %[[dummy1:.*]] = "test.dummy_op"(%[[t1]])
44  %0 = "test.dummy_op"(%t1) : (tensor<?xf32>) -> tensor<?xf32>
45  // CHECK: %[[dummy2:.*]] = "test.another_dummy_op"(%[[dummy1]])
46  %1 = "test.another_dummy_op"(%0) : (tensor<?xf32>) -> tensor<?xf32>
47
48  // CHECK: return %[[dummy2]]
49  return %1 : tensor<?xf32>
50}
51
52// -----
53
54// CHECK: #[[$MAP2:.*]] = affine_map<(d0)[s0, s1] -> (d0 * s1 + s0)>
55
56// CHECK-LABEL: func @use_of_unknown_op_3(
57//  CHECK-SAME:     %[[t1:.*]]: tensor<?xf32>
58func.func @use_of_unknown_op_3(%t1: tensor<?xf32>)
59    -> (vector<5xf32>, vector<5xf32>) {
60  %idx = arith.constant 0 : index
61  %cst = arith.constant 0.0 : f32
62  // CHECK: %[[m1:.*]] = bufferization.to_memref %[[t1]]
63  // CHECK: %[[v1:.*]] = vector.transfer_read %[[m1]]
64  %1 = vector.transfer_read %t1[%idx], %cst : tensor<?xf32>, vector<5xf32>
65
66  // CHECK: %[[dummy:.*]] = "test.dummy_op"(%[[t1]])
67  %0 = "test.dummy_op"(%t1) : (tensor<?xf32>) -> tensor<?xf32>
68  // CHECK: %[[dummy_memref:.*]] = bufferization.to_memref %[[dummy]] : memref<?xf32, #[[$MAP2]]>
69  // CHECK: %[[v2:.*]] = vector.transfer_read %[[dummy_memref]]
70  %2 = vector.transfer_read %0[%idx], %cst : tensor<?xf32>, vector<5xf32>
71
72  // CHECK: return %[[v1]], %[[v2]]
73  return %1, %2 : vector<5xf32>, vector<5xf32>
74}
75
76// -----
77
78// CHECK-LABEL: func @use_of_unknown_op_4(
79//  CHECK-SAME:     %[[t1:.*]]: tensor<?xf32>
80func.func @use_of_unknown_op_4(%t1: tensor<?xf32>)
81    -> (vector<5xf32>, tensor<?xf32>) {
82  %idx = arith.constant 0 : index
83  %cst = arith.constant 0.0 : f32
84
85  // CHECK: %[[dummy:.*]] = "test.dummy_op"(%[[t1]])
86  %0 = "test.dummy_op"(%t1) : (tensor<?xf32>) -> tensor<?xf32>
87
88  // CHECK: %[[dummy_memref:.*]] = bufferization.to_memref %[[dummy]]
89  // CHECK: %[[v1:.*]] = vector.transfer_read %[[dummy_memref]]
90  %1 = vector.transfer_read %0[%idx], %cst : tensor<?xf32>, vector<5xf32>
91
92  // CHECK: %[[another_dummy:.*]] = "test.another_dummy_op"(%[[dummy]])
93  %2 = "test.another_dummy_op"(%0) : (tensor<?xf32>) -> tensor<?xf32>
94
95  // CHECK: return %[[v1]], %[[another_dummy]]
96  return %1, %2 : vector<5xf32>, tensor<?xf32>
97}
98
99// -----
100
101// CHECK-LABEL: func @use_of_bufferizable_op_in_unbufferizable_op
102//  CHECK-SAME:     %[[t1:.*]]: tensor<?xf32>
103func.func @use_of_bufferizable_op_in_unbufferizable_op(
104    %t1: tensor<?xf32>, %o: index, %s: index) -> (tensor<?xf32>, tensor<?xf32>) {
105  // CHECK: %[[m1:.*]] = bufferization.to_memref %[[t1]]
106  // CHECK: %[[subview:.*]] = memref.subview %[[m1]]
107  %0 = tensor.extract_slice %t1[%o][%s][1] : tensor<?xf32> to tensor<?xf32>
108  // CHECK: %[[subview_tensor:.*]] = bufferization.to_tensor %[[subview]]
109  // CHECK: %[[dummy:.*]] = "test.dummy_op"(%[[subview_tensor]])
110  %1 = "test.dummy_op"(%0) : (tensor<?xf32>) -> tensor<?xf32>
111  // CHECK: return %[[subview_tensor]], %[[dummy]]
112  return %0, %1 : tensor<?xf32>, tensor<?xf32>
113}
114
115// -----
116
117// CHECK-LABEL: func @unused_unknown_op(
118//  CHECK-SAME:     %[[t1:.*]]: tensor<?xf32>
119func.func @unused_unknown_op(%t1 : tensor<?xf32>) -> vector<5xf32> {
120  %idx = arith.constant 0 : index
121  %cst = arith.constant 0.0 : f32
122
123  // CHECK: %[[m1:.*]] = bufferization.to_memref %[[t1]]
124  // CHECK: vector.transfer_read %[[m1]]
125  %1 = vector.transfer_read %t1[%idx], %cst : tensor<?xf32>, vector<5xf32>
126
127  // CHECK: "test.dummy_op"(%[[t1]])
128  "test.dummy_op"(%t1) : (tensor<?xf32>) -> ()
129
130  return %1 : vector<5xf32>
131}
132
133// -----
134
135// CHECK-LABEL: func @unknown_op_may_read(
136func.func @unknown_op_may_read(%v: vector<5xf32>)
137    -> (tensor<10xf32>, tensor<10xf32>) {
138  %idx = arith.constant 0 : index
139  %cst = arith.constant 5.0 : f32
140
141  // One alloc for the alloc_tensor, another one because the transfer_write
142  // bufferizes out-of-place.
143  // CHECK: %[[m1:.*]] = memref.alloc() {{.*}} : memref<10xf32>
144  // CHECK: linalg.fill ins(%{{.*}}{{.*}}outs(%[[m1]]
145  // CHECK: %[[filled_tensor:.*]] = bufferization.to_tensor %[[m1]]
146  %t1 = bufferization.alloc_tensor() : tensor<10xf32>
147  %filled = linalg.fill ins(%cst : f32) outs(%t1 : tensor<10xf32>) -> tensor<10xf32>
148
149  // The transfer_write is out-of-place because "dummy_op" may read.
150  // CHECK: %[[alloc:.*]] = memref.alloc() {{.*}} : memref<10xf32>
151  // CHECK: memref.copy %[[m1]], %[[alloc]]
152  // CHECK: vector.transfer_write %{{.*}}, %[[alloc]]
153  // CHECK: %[[alloc_tensor:.*]] = bufferization.to_tensor %[[alloc]]
154  %1 = vector.transfer_write %v, %filled[%idx] : vector<5xf32>, tensor<10xf32>
155
156  // CHECK: %[[dummy:.*]] = "test.dummy_op"(%[[filled_tensor]])
157  %2 = "test.dummy_op"(%filled) : (tensor<10xf32>) -> (tensor<10xf32>)
158
159  // CHECK-DAG: memref.dealloc %[[alloc]]
160  // CHECK-DAG: memref.dealloc %[[m1]]
161  // CHECK: return %[[alloc_tensor]], %[[dummy]]
162  return %1, %2 : tensor<10xf32>, tensor<10xf32>
163}
164
165// -----
166
167// CHECK-LABEL: func @unknown_op_not_writable
168//  CHECK-SAME:     %[[t1:.*]]: tensor<?xf32>
169func.func @unknown_op_not_writable(
170    %t1 : tensor<?xf32>, %v :  vector<5xf32>, %idx : index) -> tensor<?xf32> {
171  // CHECK: %[[dummy:.*]] = "test.dummy_op"(%[[t1]])
172  // CHECK: %[[dummy_memref:.*]] = bufferization.to_memref %[[dummy]]
173  %0 = "test.dummy_op"(%t1) : (tensor<?xf32>) -> (tensor<?xf32>)
174
175  // The result of an unknown op is not writable. Always generate a copy.
176  // CHECK: %[[dim:.*]] = tensor.dim %[[dummy]]
177  // CHECK: %[[alloc:.*]] = memref.alloc(%[[dim]])
178  // CHECK: memref.copy %[[dummy_memref]], %[[alloc]]
179  // CHECK: vector.transfer_write %{{.*}}, %[[alloc]]
180  %1 = vector.transfer_write %v, %0[%idx] : vector<5xf32>, tensor<?xf32>
181
182  // CHECK: %[[alloc_tensor:.*]] = bufferization.to_tensor %[[alloc]]
183  // CHECK: return %[[alloc_tensor]]
184  return %1 : tensor<?xf32>
185}
186
187// -----
188
189// CHECK-TENSOR-LABEL: func @simple_tensor_test(
190//  CHECK-TENSOR-SAME:     %[[t1:.*]]: tensor<?xf32>
191func.func @simple_tensor_test(%t1 : tensor<?xf32>, %f : f32) -> tensor<?xf32> {
192  // CHECK-TENSOR: %[[t1_memref:.*]] = bufferization.to_memref %[[t1]]
193  %c0 = arith.constant 0 : index
194  // CHECK-TENSOR: %[[alloc:.*]] = memref.alloc
195  // CHECK-TENSOR: memref.copy %[[t1_memref]], %[[alloc]]
196  // CHECK-TENSOR: memref.store %{{.*}}, %[[alloc]]
197  %0 = tensor.insert %f into %t1[%c0] : tensor<?xf32>
198  // CHECK-TENSOR: %[[casted_alloc:.*]] = bufferization.to_tensor %[[alloc]]
199  // CHECK-TENSOR: return %[[casted_alloc]]
200  return %0 : tensor<?xf32>
201}
202
203// -----
204
205// CHECK-SCF-LABEL: func @simple_scf_if(
206//  CHECK-SCF-SAME:     %[[t1:.*]]: tensor<?xf32> {bufferization.writable = true}, %[[c:.*]]: i1, %[[pos:.*]]: index
207func.func @simple_scf_if(%t1: tensor<?xf32> {bufferization.writable = true}, %c: i1, %pos: index, %f: f32)
208    -> (tensor<?xf32>, index) {
209  // CHECK-SCF: %[[t1_memref:.*]] = bufferization.to_memref %[[t1]]
210  // CHECK-SCF: %[[r:.*]] = scf.if %[[c]] -> (memref<?xf32, #{{.*}}>) {
211  %r1, %r2 = scf.if %c -> (tensor<?xf32>, index) {
212    // CHECK-SCF: scf.yield %[[t1_memref]]
213    scf.yield %t1, %pos : tensor<?xf32>, index
214  // CHECK-SCF: } else {
215  } else {
216    // CHECK-SCF: %[[insert:.*]] = tensor.insert %{{.*}} into %[[t1]][{{.*}}]
217    // CHECK-SCF: %[[insert_memref:.*]] = bufferization.to_memref %[[insert]]
218    %1 = tensor.insert %f into %t1[%pos] : tensor<?xf32>
219    // CHECK-SCF: scf.yield %[[insert_memref]]
220    scf.yield %1, %pos : tensor<?xf32>, index
221  }
222
223  // CHECK-SCF: %[[r_tensor:.*]] = bufferization.to_tensor %[[r]]
224  // CHECK-SCF: return %[[r_tensor]], %[[pos]]
225  return %r1, %r2 : tensor<?xf32>, index
226}
227