xref: /llvm-project/mlir/test/Dialect/Bufferization/Transforms/one-shot-bufferize-analysis.mlir (revision ced2fc7819d5ddea616ec330f18e08ff284c1868)
1// RUN: mlir-opt %s -one-shot-bufferize="test-analysis-only" \
2// RUN:     -allow-unregistered-dialect -split-input-file | FileCheck %s
3
4// RUN: mlir-opt %s -one-shot-bufferize="test-analysis-only dump-alias-sets" \
5// RUN:     -allow-unregistered-dialect -split-input-file | \
6// RUN: FileCheck %s --check-prefix=CHECK-ALIAS-SETS
7
8// CHECK-LABEL: func @unknown_op_aliasing(
9// CHECK-ALIAS-SETS-LABEL: func @unknown_op_aliasing(
10func.func @unknown_op_aliasing(%f: f32, %f2: f32, %pos: index) -> f32 {
11  // CHECK-ALIAS-SETS: %[[empty:.*]] = tensor.empty
12
13  %0 = tensor.empty() : tensor<10xf32>
14  // CHECK: linalg.fill {__inplace_operands_attr__ = ["none", "true"]}
15  // CHECK-ALIAS-SETS: %[[fill1:.*]] = linalg.fill
16  %1 = linalg.fill ins(%f : f32) outs(%0 : tensor<10xf32>) -> tensor<10xf32>
17
18  // Something must bufferize out-of-place because the op may return an alias
19  // of %1.
20  // CHECK: "dummy.dummy_op"(%{{.*}}) {__inplace_operands_attr__ = ["false"]}
21  %alias = "dummy.dummy_op"(%1) : (tensor<10xf32>) -> (tensor<10xf32>)
22
23  // CHECK: linalg.fill {__inplace_operands_attr__ = ["none", "true"]}
24  // CHECK-ALIAS-SETS: %[[fill2:.*]] = linalg.fill
25  // CHECK-ALIAS-SETS-SAME: __opresult_alias_set_attr__ = [{{\[}}"%[[fill2]]", "%[[fill1]]", "%[[empty]]"]]
26  %2 = linalg.fill ins(%f2 : f32) outs(%1 : tensor<10xf32>) -> tensor<10xf32>
27  %3 = tensor.extract %alias[%pos] : tensor<10xf32>
28  return %3 : f32
29}
30
31// -----
32
33// CHECK-LABEL: func @unknown_op_bbarg_aliasing(
34// CHECK-ALIAS-SETS-LABEL: func @unknown_op_bbarg_aliasing(
35func.func @unknown_op_bbarg_aliasing() {
36  %0 = tensor.empty() : tensor<7xf32>
37
38  // %arg0 is not aliasing with %0 because it bufferizes out-of-place.
39  // CHECK-ALIAS-SETS: "dummy.dummy_op"
40  // CHECK-ALIAS-SETS-NEXT: ^{{.*}}(%[[arg:.*]]: tensor<7xf32>):
41  // CHECK-ALIAS-SETS-NEXT: }) {__bbarg_alias_set_attr__ = [{{\[}}[{{\[}}"%[[arg]]"]]]], __inplace_operands_attr__ = ["false"]} : (tensor<7xf32>) -> ()
42  "dummy.dummy_op"(%0) ({
43  ^bb0(%arg1: tensor<7xf32>):
44  }) : (tensor<7xf32>) -> ()
45  return
46}
47
48// -----
49
50// CHECK-LABEL: func @unknown_op_writing(
51func.func @unknown_op_writing(%f: f32, %f2: f32, %pos: index) -> f32 {
52  %0 = tensor.empty() : tensor<10xf32>
53  // CHECK: linalg.fill {__inplace_operands_attr__ = ["none", "true"]}
54  %1 = linalg.fill ins(%f : f32) outs(%0 : tensor<10xf32>) -> tensor<10xf32>
55
56  // The op may bufferize to a memory write, so it must bufferize out-of-place.
57  // CHECK: "dummy.dummy_op"(%{{.*}}) {__inplace_operands_attr__ = ["false"]}
58  "dummy.dummy_op"(%1) : (tensor<10xf32>) -> ()
59
60  %3 = tensor.extract %1[%pos] : tensor<10xf32>
61  return %3 : f32
62}
63
64// -----
65
66// CHECK-LABEL: func @read_of_undef_is_not_a_conflict(
67func.func @read_of_undef_is_not_a_conflict(%f: f32, %idx: index) -> f32 {
68  %0 = tensor.empty() : tensor<10xf32>
69  // This can be in-place because the read below does reads undefined data.
70  // CHECK: tensor.insert {{.*}} {__inplace_operands_attr__ = ["none", "true", "none"]}
71  %1 = tensor.insert %f into %0[%idx] : tensor<10xf32>
72  %2 = tensor.extract %0[%idx] : tensor<10xf32>
73  return %2 : f32
74}
75
76// -----
77
78// CHECK-LABEL: func @read_of_alloc_tensor_is_not_a_conflict(
79func.func @read_of_alloc_tensor_is_not_a_conflict(%f: f32, %idx: index) -> f32 {
80  %0 = bufferization.alloc_tensor() : tensor<10xf32>
81  // This can be in-place because the read below does reads undefined data.
82  // CHECK: tensor.insert {{.*}} {__inplace_operands_attr__ = ["none", "true", "none"]}
83  %1 = tensor.insert %f into %0[%idx] : tensor<10xf32>
84  %2 = tensor.extract %0[%idx] : tensor<10xf32>
85  return %2 : f32
86}
87
88// -----
89
90// CHECK-LABEL: func @to_memref_not_read_only(
91func.func @to_memref_not_read_only(%idx : index, %f: f32) -> f32 {
92  %t = tensor.generate {
93  ^bb0(%i : index):
94    tensor.yield %f : f32
95  } : tensor<5xf32>
96  // Some op may write into the result of to_memref later.
97  // CHECK: bufferization.to_memref
98  // CHECK-SAME: {__inplace_operands_attr__ = ["false"]}
99  %m = bufferization.to_memref %t : tensor<5xf32> to memref<5xf32>
100  %2 = tensor.extract %t[%idx] : tensor<5xf32>
101  return %2 : f32
102}
103
104// -----
105
106// CHECK-LABEL: func @to_memref_read_only(
107func.func @to_memref_read_only(%idx : index, %f: f32) -> f32 {
108  %t = tensor.generate {
109  ^bb0(%i : index):
110    tensor.yield %f : f32
111  } : tensor<5xf32>
112  // Some op may write into the result of to_memref later.
113  // CHECK: bufferization.to_memref
114  // CHECK-SAME: {__inplace_operands_attr__ = ["true"]}
115  %m = bufferization.to_memref %t {read_only} : tensor<5xf32> to memref<5xf32>
116  %2 = tensor.extract %t[%idx] : tensor<5xf32>
117  return %2 : f32
118}
119
120// -----
121
122// CHECK-LABEL: func @bbarg_of_unknown_op(
123func.func @bbarg_of_unknown_op(%f: f32) {
124  %0 = tensor.empty() : tensor<10xf32>
125  // CHECK: linalg.fill {__inplace_operands_attr__ = ["none", "true"]}
126  %1 = linalg.fill ins(%f : f32) outs(%0 : tensor<10xf32>) -> tensor<10xf32>
127
128  // The op is not bufferizable because %1 is assumed to alias with %arg1.
129  // BlockArguments are considered "not writable" by default. So %1 is also
130  // considered "not writable".
131
132  // CHECK: "dummy.dummy_op"
133  // CHECK: {__inplace_operands_attr__ = ["false"]} : (tensor<10xf32>) -> ()
134  "dummy.dummy_op"(%1) ({
135  ^bb0(%arg1: tensor<10xf32>):
136  }) : (tensor<10xf32>) -> ()
137  return
138}
139
140// -----
141
142// CHECK-LABEL: func @bbarg_of_unknown_op_2(
143func.func @bbarg_of_unknown_op_2(%f: f32) {
144  %0 = tensor.empty() : tensor<10xf32>
145  // CHECK: linalg.fill {__inplace_operands_attr__ = ["none", "true"]}
146  %1 = linalg.fill ins(%f : f32) outs(%0 : tensor<10xf32>) -> tensor<10xf32>
147
148  // The op is not bufferizable because %1 is assumed to alias with %arg1.
149  // BlockArguments are considered "not writable" by default. So %1 is also
150  // considered "not writable".
151
152  // CHECK: "dummy.dummy_op"
153  "dummy.dummy_op"(%1) ({
154  ^bb0(%arg1: tensor<10xf32>):
155    // CHECK: "dummy.another_op"(%{{.*}}) {__inplace_operands_attr__ = ["false"]}
156    "dummy.another_op"(%arg1) : (tensor<10xf32>) -> ()
157  }) : (tensor<10xf32>) -> ()
158  // CHECK: {__inplace_operands_attr__ = ["false"]} : (tensor<10xf32>) -> ()
159  return
160}
161
162// -----
163
164// CHECK: func @materialize_in_destination_aliasing(
165func.func @materialize_in_destination_aliasing(%t: tensor<?xf32>, %p1: index, %p2: index, %sz: index) -> tensor<5xf32> {
166  %buffer = tensor.empty(%sz) : tensor<?xf32>
167  // CHECK: tensor.extract_slice
168  // CHECK-SAME: {__inplace_operands_attr__ = ["true", "none"]}
169  %src = tensor.extract_slice %t[%p1][5][1] : tensor<?xf32> to tensor<5xf32>
170  // CHECK: tensor.extract_slice
171  // CHECK-SAME: {__inplace_operands_attr__ = ["false", "none"]}
172  %dest = tensor.extract_slice %t[%p2][5][1] : tensor<?xf32> to tensor<5xf32>
173  // CHECK: bufferization.materialize_in_destination
174  // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true"]}
175  %r = bufferization.materialize_in_destination %src in %dest : (tensor<5xf32>, tensor<5xf32>) -> tensor<5xf32>
176  return %r : tensor<5xf32>
177}
178
179// -----
180
181// CHECK: func @materialize_in_destination(
182func.func @materialize_in_destination(%t: tensor<?xf32>, %sz: index) -> tensor<?xf32> {
183  %buffer = tensor.empty(%sz) : tensor<?xf32>
184  // CHECK: bufferization.materialize_in_destination
185  // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true"]}
186  %r = bufferization.materialize_in_destination %buffer in %buffer : (tensor<?xf32>, tensor<?xf32>) -> tensor<?xf32>
187  return %r : tensor<?xf32>
188}
189