1// RUN: mlir-opt %s -allow-unregistered-dialect -one-shot-bufferize="bufferize-function-boundaries=1" -split-input-file -verify-diagnostics 2 3func.func private @foo() -> tensor<?xf32> 4 5func.func @bar() -> tensor<?xf32> { 6 %foo = constant @foo : () -> (tensor<?xf32>) 7// expected-error @+1 {{expected a CallOp}} 8 %res = call_indirect %foo() : () -> (tensor<?xf32>) 9 return %res : tensor<?xf32> 10} 11 12// ----- 13 14// expected-error @+2 {{cannot bufferize bodiless function that returns a tensor}} 15// expected-error @+1 {{failed to bufferize op}} 16func.func private @foo() -> tensor<?xf32> 17 18// ----- 19 20// expected-error @+1 {{cannot bufferize a FuncOp with tensors and without a unique ReturnOp}} 21func.func @swappy(%cond1 : i1, %cond2 : i1, %t1 : tensor<f32>, %t2 : tensor<f32>) 22 -> (tensor<f32>, tensor<f32>) 23{ 24 cf.cond_br %cond1, ^bb1, ^bb2 25 26 ^bb1: 27 %T:2 = scf.if %cond2 -> (tensor<f32>, tensor<f32>) { 28 scf.yield %t1, %t2 : tensor<f32>, tensor<f32> 29 } else { 30 scf.yield %t2, %t1 : tensor<f32>, tensor<f32> 31 } 32 return %T#0, %T#1 : tensor<f32>, tensor<f32> 33 ^bb2: 34 return %t2, %t1 : tensor<f32>, tensor<f32> 35} 36 37// ----- 38 39func.func @scf_if_not_equivalent( 40 %cond: i1, %t1: tensor<?xf32> {bufferization.writable = true}, 41 %idx: index) -> tensor<?xf32> { 42 %r = scf.if %cond -> (tensor<?xf32>) { 43 scf.yield %t1 : tensor<?xf32> 44 } else { 45 // This buffer aliases, but it is not equivalent. 46 %t2 = tensor.extract_slice %t1 [%idx] [%idx] [1] : tensor<?xf32> to tensor<?xf32> 47 // expected-error @+1 {{operand #0 of ReturnLike op does not satisfy destination passing style}} 48 scf.yield %t2 : tensor<?xf32> 49 } 50 // expected-error @+1 {{operand #0 of ReturnLike op does not satisfy destination passing style}} 51 return %r : tensor<?xf32> 52} 53 54// ----- 55 56func.func @scf_if_not_aliasing( 57 %cond: i1, %t1: tensor<?xf32> {bufferization.writable = true}, 58 %idx: index) -> f32 { 59 %r = scf.if %cond -> (tensor<?xf32>) { 60 scf.yield %t1 : tensor<?xf32> 61 } else { 62 // This buffer aliases. 63 %t2 = bufferization.alloc_tensor(%idx) : tensor<?xf32> 64 // expected-error @+1 {{operand #0 of ReturnLike op does not satisfy destination passing style}} 65 scf.yield %t2 : tensor<?xf32> 66 } 67 %f = tensor.extract %r[%idx] : tensor<?xf32> 68 return %f : f32 69} 70 71// ----- 72 73// expected-error @-3 {{expected callgraph to be free of circular dependencies}} 74 75func.func @foo() { 76 call @bar() : () -> () 77 return 78} 79 80func.func @bar() { 81 call @foo() : () -> () 82 return 83} 84 85// ----- 86 87func.func @scf_for(%A : tensor<?xf32>, 88 %B : tensor<?xf32> {bufferization.writable = true}, 89 %C : tensor<4xf32>, 90 %lb : index, %ub : index, %step : index) 91 -> (f32, f32) 92{ 93 %r0:2 = scf.for %i = %lb to %ub step %step iter_args(%tA = %A, %tB = %B) 94 -> (tensor<?xf32>, tensor<?xf32>) 95 { 96 %ttA = tensor.insert_slice %C into %tA[0][4][1] : tensor<4xf32> into tensor<?xf32> 97 %ttB = tensor.insert_slice %C into %tB[0][4][1] : tensor<4xf32> into tensor<?xf32> 98 99 // Throw a wrench in the system by swapping yielded values: this result in a 100 // ping-pong of values at each iteration on which we currently want to fail. 101 102 // expected-error @+1 {{Yield operand #0 is not equivalent to the corresponding iter bbArg}} 103 scf.yield %ttB, %ttA : tensor<?xf32>, tensor<?xf32> 104 } 105 106 %f0 = tensor.extract %r0#0[%step] : tensor<?xf32> 107 %f1 = tensor.extract %r0#1[%step] : tensor<?xf32> 108 return %f0, %f1: f32, f32 109} 110 111// ----- 112 113func.func @scf_while_non_equiv_condition(%arg0: tensor<5xi1>, 114 %arg1: tensor<5xi1>, 115 %idx: index) -> (i1, i1) 116{ 117 %r0, %r1 = scf.while (%w0 = %arg0, %w1 = %arg1) 118 : (tensor<5xi1>, tensor<5xi1>) -> (tensor<5xi1>, tensor<5xi1>) { 119 %condition = tensor.extract %w0[%idx] : tensor<5xi1> 120 // expected-error @+1 {{Condition arg #0 is not equivalent to the corresponding iter bbArg}} 121 scf.condition(%condition) %w1, %w0 : tensor<5xi1>, tensor<5xi1> 122 } do { 123 ^bb0(%b0: tensor<5xi1>, %b1: tensor<5xi1>): 124 %pos = "dummy.some_op"() : () -> (index) 125 %val = "dummy.another_op"() : () -> (i1) 126 %1 = tensor.insert %val into %b0[%pos] : tensor<5xi1> 127 scf.yield %1, %b1 : tensor<5xi1>, tensor<5xi1> 128 } 129 130 %v0 = tensor.extract %r0[%idx] : tensor<5xi1> 131 %v1 = tensor.extract %r1[%idx] : tensor<5xi1> 132 return %v0, %v1 : i1, i1 133} 134 135// ----- 136 137func.func @scf_while_non_equiv_yield(%arg0: tensor<5xi1>, 138 %arg1: tensor<5xi1>, 139 %idx: index) -> (i1, i1) 140{ 141 %r0, %r1 = scf.while (%w0 = %arg0, %w1 = %arg1) 142 : (tensor<5xi1>, tensor<5xi1>) -> (tensor<5xi1>, tensor<5xi1>) { 143 %condition = tensor.extract %w0[%idx] : tensor<5xi1> 144 scf.condition(%condition) %w0, %w1 : tensor<5xi1>, tensor<5xi1> 145 } do { 146 ^bb0(%b0: tensor<5xi1>, %b1: tensor<5xi1>): 147 %pos = "dummy.some_op"() : () -> (index) 148 %val = "dummy.another_op"() : () -> (i1) 149 %1 = tensor.insert %val into %b0[%pos] : tensor<5xi1> 150 // expected-error @+1 {{Yield operand #0 is not equivalent to the corresponding iter bbArg}} 151 scf.yield %b1, %1 : tensor<5xi1>, tensor<5xi1> 152 } 153 154 %v0 = tensor.extract %r0[%idx] : tensor<5xi1> 155 %v1 = tensor.extract %r1[%idx] : tensor<5xi1> 156 return %v0, %v1 : i1, i1 157} 158 159// ----- 160 161func.func private @fun_with_side_effects(%A: tensor<?xf32> {bufferization.writable = true}) 162 163func.func @foo(%A: tensor<?xf32> {bufferization.writable = true}) -> (tensor<?xf32>) { 164 call @fun_with_side_effects(%A) : (tensor<?xf32>) -> () 165 return %A: tensor<?xf32> 166} 167 168func.func @scf_yield_needs_copy(%A : tensor<?xf32> {bufferization.writable = true}, %iters : index) { 169 %c0 = arith.constant 0 : index 170 %c1 = arith.constant 1 : index 171 %res = scf.for %arg0 = %c0 to %iters step %c1 iter_args(%bbarg = %A) -> (tensor<?xf32>) { 172 %r = func.call @foo(%A) : (tensor<?xf32>) -> (tensor<?xf32>) 173 // expected-error @+1 {{Yield operand #0 is not equivalent to the corresponding iter bbArg}} 174 scf.yield %r : tensor<?xf32> 175 } 176 call @fun_with_side_effects(%res) : (tensor<?xf32>) -> () 177 return 178} 179 180// ----- 181 182func.func @extract_slice_fun(%A : tensor<?xf32> {bufferization.writable = true}) 183 -> tensor<4xf32> 184{ 185 // This bufferizes to a pattern that the cross-function boundary pass needs to 186 // convert into a new memref argument at all call site; this may be either: 187 // - an externally created aliasing subview (if we want to allow aliasing 188 // function arguments). 189 // - a new alloc + copy (more expensive but does not create new function 190 // argument aliasing). 191 %r0 = tensor.extract_slice %A[0][4][1] : tensor<?xf32> to tensor<4xf32> 192 193 // expected-error @+1 {{operand #0 of ReturnLike op does not satisfy destination passing style}} 194 return %r0: tensor<4xf32> 195} 196 197// ----- 198 199func.func @scf_yield(%b : i1, %A : tensor<4xf32>, %B : tensor<4xf32>) -> tensor<4xf32> 200{ 201 %r = scf.if %b -> (tensor<4xf32>) { 202 scf.yield %A : tensor<4xf32> 203 } else { 204 scf.yield %B : tensor<4xf32> 205 } 206 // expected-error @+1 {{operand #0 of ReturnLike op does not satisfy destination passing style}} 207 return %r: tensor<4xf32> 208} 209 210// ----- 211 212func.func @unknown_op(%A : tensor<4xf32>) -> tensor<4xf32> 213{ 214 // expected-error: @+1 {{op was not bufferized}} 215 %r = "marklar"(%A) : (tensor<4xf32>) -> (tensor<4xf32>) 216 // expected-error @+1 {{operand #0 of ReturnLike op does not satisfy destination passing style}} 217 return %r: tensor<4xf32> 218} 219 220// ----- 221 222func.func @mini_test_case1() -> tensor<10x20xf32> { 223 %f0 = arith.constant 0.0 : f32 224 %t = bufferization.alloc_tensor() : tensor<10x20xf32> 225 %r = linalg.fill ins(%f0 : f32) outs(%t : tensor<10x20xf32>) -> tensor<10x20xf32> 226 // expected-error @+1 {{operand #0 of ReturnLike op does not satisfy destination passing style}} 227 return %r : tensor<10x20xf32> 228} 229 230// ----- 231 232func.func @main() -> tensor<4xi32> { 233 %r = scf.execute_region -> tensor<4xi32> { 234 %A = arith.constant dense<[1, 2, 3, 4]> : tensor<4xi32> 235 // expected-error @+1 {{operand #0 of ReturnLike op does not satisfy destination passing style}} 236 scf.yield %A: tensor<4xi32> 237 } 238 239 // expected-error @+1 {{operand #0 of ReturnLike op does not satisfy destination passing style}} 240 return %r: tensor<4xi32> 241} 242 243// ----- 244 245func.func @to_memref_op_is_writing( 246 %t1: tensor<?xf32> {bufferization.writable = true}, %idx1: index, 247 %idx2: index, %idx3: index, %v1: vector<5xf32>) -> (vector<5xf32>, vector<5xf32>) { 248 // This is a RaW conflict because to_memref is an inplace write and %t1 is 249 // read further down. This will likely have to change with partial 250 // bufferization. 251 252 // expected-error @+1 {{input IR has RaW conflict}} 253 %0 = bufferization.to_memref %t1 : memref<?xf32> 254 255 // Read from both. 256 %cst = arith.constant 0.0 : f32 257 %r1 = vector.transfer_read %t1[%idx3], %cst : tensor<?xf32>, vector<5xf32> 258 %r2 = vector.transfer_read %0[%idx3], %cst : memref<?xf32>, vector<5xf32> 259 260 return %r1, %r2 : vector<5xf32>, vector<5xf32> 261} 262 263// ----- 264 265// expected-error @+2 {{failed to bufferize op}} 266// expected-error @+1 {{cannot bufferize bodiless function that returns a tensor}} 267func.func private @foo(%t : tensor<?xf32>) -> (f32, tensor<?xf32>, f32) 268 269func.func @call_to_unknown_tensor_returning_func(%t : tensor<?xf32>) { 270 call @foo(%t) : (tensor<?xf32>) -> (f32, tensor<?xf32>, f32) 271 return 272} 273 274// ----- 275 276func.func @foo(%t : tensor<5xf32>) -> (tensor<5xf32>) { 277 %0 = bufferization.alloc_tensor() : tensor<5xf32> 278 // expected-error @+1 {{operand #0 of ReturnLike op does not satisfy destination passing style}} 279 return %0 : tensor<5xf32> 280} 281 282// Note: This function is not analyzed because there was an error in the 283// previous one. 284func.func @call_to_func_returning_non_equiv_tensor(%t : tensor<5xf32>) { 285 call @foo(%t) : (tensor<5xf32>) -> (tensor<5xf32>) 286 return 287} 288 289// ----- 290 291func.func @destination_passing_style_dominance_test_1(%cst : f32, %idx : index, 292 %idx2 : index) -> f32 { 293 %0 = scf.execute_region -> tensor<?xf32> { 294 %1 = bufferization.alloc_tensor(%idx) : tensor<?xf32> 295 // expected-error @+1 {{operand #0 of ReturnLike op does not satisfy destination passing style}} 296 scf.yield %1 : tensor<?xf32> 297 } 298 %2 = tensor.insert %cst into %0[%idx] : tensor<?xf32> 299 %r = tensor.extract %2[%idx2] : tensor<?xf32> 300 return %r : f32 301} 302 303// ----- 304 305func.func @destination_passing_style_dominance_test_2(%cst : f32, %idx : index, 306 %idx2 : index) -> f32 { 307 %1 = bufferization.alloc_tensor(%idx) : tensor<?xf32> 308 309 %0 = scf.execute_region -> tensor<?xf32> { 310 // This YieldOp is in destination-passing style, thus no error. 311 scf.yield %1 : tensor<?xf32> 312 } 313 %2 = tensor.insert %cst into %0[%idx] : tensor<?xf32> 314 %r = tensor.extract %2[%idx2] : tensor<?xf32> 315 return %r : f32 316} 317