1428 lines · plain
1// RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries test-analysis-only" -split-input-file | FileCheck %s2// RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries test-analysis-only dump-alias-sets" -split-input-file | FileCheck %s --check-prefix=CHECK-ALIAS3 4// Run fuzzer with different seeds.5// RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries test-analysis-only analysis-heuristic=fuzzer analysis-fuzzer-seed=23" -split-input-file -o /dev/null6// RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries test-analysis-only analysis-heuristic=fuzzer analysis-fuzzer-seed=59" -split-input-file -o /dev/null7// RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries test-analysis-only analysis-heuristic=fuzzer analysis-fuzzer-seed=91" -split-input-file -o /dev/null8 9// Try different heuristics. Not checking the result, just make sure that we do10// not crash.11// RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries test-analysis-only analysis-heuristic=bottom-up-from-terminators" -split-input-file -o /dev/null12// RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries test-analysis-only analysis-heuristic=top-down" -split-input-file -o /dev/null13 14// TODO: Extract op-specific test cases and move them to their respective15// dialects.16 17//===----------------------------------------------------------------------===//18// Simple cases19//===----------------------------------------------------------------------===//20 21// -----22 23// CHECK-LABEL: func @extract_slice_fun(24func.func @extract_slice_fun(%A : tensor<?xf32> {bufferization.writable = false},25// CHECK-SAME: bufferization.access = "read"26 %B : tensor<?xf32> {bufferization.writable = true})27// CHECK-SAME: bufferization.access = "read"28 -> (tensor<4xf32>, tensor<8xf32>)29{30 // tensor.extract_slice is not used in a write, it is not compelled to31 // bufferize out of place. Let callers decide whether they want to create32 // aliasing subviews at all call sites or whether they allocate.33 // This is true irrespective of whether the function argument is inplaceable.34 // CHECK: tensor.extract_slice35 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]}36 %r0 = tensor.extract_slice %A[0][4][1] : tensor<?xf32> to tensor<4xf32>37 38 // CHECK: tensor.extract_slice39 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]}40 %r1 = tensor.extract_slice %B[0][8][1] : tensor<?xf32> to tensor<8xf32>41 42 return %r0, %r1: tensor<4xf32>, tensor<8xf32>43}44 45// -----46 47// CHECK-LABEL: func @insert_slice_fun(48func.func @insert_slice_fun(%A : tensor<?xf32> {bufferization.writable = false},49// CHECK-SAME: bufferization.access = "read"50 %B : tensor<?xf32> {bufferization.writable = true},51// CHECK-SAME: bufferization.access = "read-write"52 %C : tensor<4xf32> {bufferization.writable = false})53// CHECK-SAME: bufferization.access = "read"54 -> (tensor<?xf32>, tensor<?xf32>)55{56 // must bufferize out of place.57 // CHECK: tensor.insert_slice58 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "false"]}59 %r0 = tensor.insert_slice %C into %A[0][4][1] : tensor<4xf32> into tensor<?xf32>60 61 // bufferizes inplace.62 // CHECK: tensor.insert_slice63 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true"]}64 %r1 = tensor.insert_slice %C into %B[0][4][1] : tensor<4xf32> into tensor<?xf32>65 66 // CHECK: return67 // CHECK-SAME: __equivalent_func_args__ = [-1, 1]68 return %r0, %r1: tensor<?xf32>, tensor<?xf32>69}70 71// -----72 73// CHECK-LABEL: func @conflict_on_B(74func.func @conflict_on_B(%A : tensor<4x4xf32> {bufferization.writable = true},75// CHECK-SAME: bufferization.access = "read"76 %B : tensor<4x4xf32> {bufferization.writable = true})77// CHECK-SAME: bufferization.access = "read-write"78 -> (tensor<4x4xf32>, tensor<4x4xf32>, tensor<4x4xf32>)79{80 // matmul output operand interferes with input operand.81 // CHECK: linalg.matmul82 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "false"]}83 %C = linalg.matmul ins(%A, %B: tensor<4x4xf32>, tensor<4x4xf32>)84 outs(%B: tensor<4x4xf32>)85 -> tensor<4x4xf32>86 87 // matmul output operand interferes with input operand.88 // CHECK: linalg.matmul89 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "false"]}90 %D = linalg.matmul ins(%B, %A: tensor<4x4xf32>, tensor<4x4xf32>)91 outs(%B: tensor<4x4xf32>)92 -> tensor<4x4xf32>93 94 // matmul output operand does not interferes with input operand.95 // CHECK: linalg.matmul96 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "true"]}97 %E = linalg.matmul ins(%A, %A: tensor<4x4xf32>, tensor<4x4xf32>)98 outs(%B: tensor<4x4xf32>)99 -> tensor<4x4xf32>100 101 // CHECK: return102 // CHECK-SAME: __equivalent_func_args__ = [-1, -1, 1]103 return %C, %D, %E: tensor<4x4xf32>, tensor<4x4xf32>, tensor<4x4xf32>104}105 106//===----------------------------------------------------------------------===//107// Length-1 producer-consumer cases.108//===----------------------------------------------------------------------===//109 110// -----111 112// CHECK-LABEL: func @extract_slice_extract_slice(113func.func @extract_slice_extract_slice(114 %A : tensor<?xf32> {bufferization.writable = true},115// CHECK-SAME: bufferization.access = "read"116 %B : tensor<?xf32> {bufferization.writable = false})117// CHECK-SAME: bufferization.access = "read"118 -> (tensor<2xf32>, tensor<2xf32>)119{120 // tensor.extract_slice is not used in a write, it is not compelled to121 // bufferize out of place. Let callers decide whether they want to create122 // aliasing subviews at all call sites or whether they allocate.123 // This is true irrespective of whether the function argument is inplaceable.124 // CHECK: {__inplace_operands_attr__ = ["true"]}125 %r0 = tensor.extract_slice %A[0][4][1] : tensor<?xf32> to tensor<4xf32>126 127 // CHECK: {__inplace_operands_attr__ = ["true"]}128 %r1 = tensor.extract_slice %r0[0][2][1] : tensor<4xf32> to tensor<2xf32>129 130 // CHECK: {__inplace_operands_attr__ = ["true"]}131 %r2 = tensor.extract_slice %B[0][4][1] : tensor<?xf32> to tensor<4xf32>132 133 // CHECK: {__inplace_operands_attr__ = ["true"]}134 %r3 = tensor.extract_slice %r2[0][2][1] : tensor<4xf32> to tensor<2xf32>135 136 return %r1, %r3: tensor<2xf32>, tensor<2xf32>137}138 139// -----140 141// CHECK-LABEL: func @insert_slice_insert_slice(142func.func @insert_slice_insert_slice(143 %A : tensor<?xf32> {bufferization.writable = true},144// CHECK-SAME: bufferization.access = "read-write"145 %A2 : tensor<4xf32> {bufferization.writable = true},146// CHECK-SAME: bufferization.access = "read-write"147 %A3 : tensor<2xf32> {bufferization.writable = true},148// CHECK-SAME: bufferization.access = "read"149 %B : tensor<?xf32> {bufferization.writable = false},150// CHECK-SAME: bufferization.access = "read"151 %B2 : tensor<4xf32> {bufferization.writable = false},152// CHECK-SAME: bufferization.access = "read"153 %B3 : tensor<2xf32> {bufferization.writable = false})154// CHECK-SAME: bufferization.access = "read"155 -> (tensor<?xf32>, tensor<?xf32>)156{157 // CHECK: {__inplace_operands_attr__ = ["true", "true"]}158 %r0 = tensor.insert_slice %A3 into %A2[0][2][1] : tensor<2xf32> into tensor<4xf32>159 160 // CHECK: {__inplace_operands_attr__ = ["true", "true"]}161 %r1 = tensor.insert_slice %r0 into %A[0][4][1] : tensor<4xf32> into tensor<?xf32>162 163 // CHECK: {__inplace_operands_attr__ = ["true", "false"]}164 %r2 = tensor.insert_slice %B3 into %B2[0][2][1] : tensor<2xf32> into tensor<4xf32>165 166 // CHECK: {__inplace_operands_attr__ = ["true", "false"]}167 %r3 = tensor.insert_slice %r2 into %B[0][4][1] : tensor<4xf32> into tensor<?xf32>168 169 // CHECK: return170 // CHECK-SAME: __equivalent_func_args__ = [0, -1]171 return %r1, %r3: tensor<?xf32>, tensor<?xf32>172}173 174// -----175 176// CHECK-LABEL: func @extract_slice_nonmatching_insert_slice177func.func @extract_slice_nonmatching_insert_slice(178 %A : tensor<?xf32> {bufferization.writable = true},179 %B : tensor<?xf32> {bufferization.writable = false},180 %idx: index)181 -> (tensor<?xf32>, tensor<?xf32>)182{183 // %r1 bufferizes inplace because %A is inplaceable.184 // %r0 is an overlapping tensor.extract_slice that does not match, it must be185 // out of place.186 // CHECK: tensor.extract_slice187 // CHECK-SAME: {__inplace_operands_attr__ = ["false"]}188 %r0 = tensor.extract_slice %A[0][4][1] : tensor<?xf32> to tensor<4xf32>189 190 // %r1 can bufferize inplace fine.191 // CHECK: tensor.insert_slice192 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "none"]}193 %r1 = tensor.insert_slice %r0 into %A[%idx][4][1] : tensor<4xf32> into tensor<?xf32>194 195 // %r3 does bufferizes inplace because %B is not inplaceable.196 // %r0 is an overlapping tensor.extract_slice that does not match, but does197 // not alias with the buffer coming from %r3 so it can actually bufferize198 // inplace.199 // CHECK: tensor.extract_slice200 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]}201 %r2 = tensor.extract_slice %B[0][4][1] : tensor<?xf32> to tensor<4xf32>202 203 // %r3 cannot bufferize inplace since %B is not inplaceable.204 // CHECK: tensor.insert_slice205 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "false", "none"]}206 %r3 = tensor.insert_slice %r2 into %B[%idx][4][1] : tensor<4xf32> into tensor<?xf32>207 208 // CHECK: return209 // CHECK-SAME: __equivalent_func_args__ = [0, -1]210 return %r1, %r3: tensor<?xf32>, tensor<?xf32>211}212 213// -----214 215// CHECK-LABEL: func @extract_slice_matching_insert_slice216func.func @extract_slice_matching_insert_slice(217 %A : tensor<?xf32> {bufferization.writable = true},218 %B : tensor<?xf32> {bufferization.writable = false})219 -> (tensor<?xf32>, tensor<?xf32>)220{221 // %r1 bufferizes inplace because %A is inplaceable.222 // %r0 is a tensor.extract_slice that matches, it can also be bufferized223 // inplace.224 // CHECK: tensor.extract_slice225 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]}226 %r0 = tensor.extract_slice %A[0][4][1] : tensor<?xf32> to tensor<4xf32>227 228 // CHECK: tensor.insert_slice229 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true"]}230 %r1 = tensor.insert_slice %r0 into %A[0][4][1] : tensor<4xf32> into tensor<?xf32>231 232 // %r2 is a tensor.extract_slice that matches %r3, it can be bufferized233 // inplace.234 // CHECK: tensor.extract_slice235 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]}236 %r2 = tensor.extract_slice %B[0][4][1] : tensor<?xf32> to tensor<4xf32>237 238 // tensor.insert_slice cannot bufferize inplace.239 // This should have been captured by a canonicalization pattern and it would240 // be unproductive to have special logic in bufferization to encode matching241 // insert_slice(extract_slice(A), A).242 // CHECK: tensor.insert_slice243 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "false"]}244 %r3 = tensor.insert_slice %r2 into %B[0][4][1] : tensor<4xf32> into tensor<?xf32>245 246 // CHECK: return247 // CHECK-SAME: __equivalent_func_args__ = [0, -1]248 return %r1, %r3: tensor<?xf32>, tensor<?xf32>249}250 251// -----252 253// CHECK-LABEL: @read_of_matching_insert_slice_source254func.func @read_of_matching_insert_slice_source(255 %A : tensor<?xf32> {bufferization.writable = true},256 %idx : index,257 %idx2 : index)258 -> (tensor<?xf32>, vector<5xf32>)259{260 %cst = arith.constant 0.0 : f32261 %cst2 = arith.constant 1.0 : f32262 263 // CHECK: tensor.extract_slice264 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "none", "none"]}265 %0 = tensor.extract_slice %A[%idx][%idx][1] : tensor<?xf32> to tensor<?xf32>266 267 // CHECK: linalg.fill268 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true"]}269 %1 = linalg.fill ins(%cst : f32) outs(%0 : tensor<?xf32>) -> tensor<?xf32>270 271 // CHECK: tensor.insert_slice272 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "none", "none"]}273 %2 = tensor.insert_slice %1 into %A[%idx][%idx][1] : tensor<?xf32> into tensor<?xf32>274 275 %3 = vector.transfer_read %1[%idx2], %cst2 : tensor<?xf32>, vector<5xf32>276 277 // CHECK: return278 // CHECK-SAME: __equivalent_func_args__ = [0, -1]279 return %2, %3 : tensor<?xf32>, vector<5xf32>280}281 282// -----283 284// CHECK-LABEL: @read_of_matching_insert_slice_source_interleaved285func.func @read_of_matching_insert_slice_source_interleaved(286 %A : tensor<?xf32> {bufferization.writable = true},287 %idx : index,288 %idx2 : index,289 %idx3 : index)290 -> (tensor<?xf32>, vector<5xf32>)291{292 %cst = arith.constant 0.0 : f32293 %cst2 = arith.constant 1.0 : f32294 295 // CHECK: tensor.extract_slice296 // CHECK-SAME: {__inplace_operands_attr__ = ["false", "none", "none"]}297 %0 = tensor.extract_slice %A[%idx][%idx][1] : tensor<?xf32> to tensor<?xf32>298 299 // CHECK: linalg.fill300 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true"]}301 %1 = linalg.fill ins(%cst : f32) outs(%0 : tensor<?xf32>) -> tensor<?xf32>302 303 // CHECK: tensor.insert_slice304 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "none", "none"]}305 %2 = tensor.insert_slice %1 into %A[%idx][%idx][1] : tensor<?xf32> into tensor<?xf32>306 307 // CHECK: tensor.extract_slice308 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "none", "none"]}309 %4 = tensor.extract_slice %2[%idx3][%idx3][1] : tensor<?xf32> to tensor<?xf32>310 311 // CHECK: linalg.fill312 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true"]}313 %5 = linalg.fill ins(%cst : f32) outs(%4 : tensor<?xf32>) -> tensor<?xf32>314 315 %3 = vector.transfer_read %1[%idx2], %cst2 : tensor<?xf32>, vector<5xf32>316 317 // CHECK: tensor.insert_slice318 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "none", "none"]}319 %6 = tensor.insert_slice %5 into %2[%idx3][%idx3][1] : tensor<?xf32> into tensor<?xf32>320 321 // CHECK: return322 // CHECK-SAME: __equivalent_func_args__ = [0, -1]323 return %6, %3 : tensor<?xf32>, vector<5xf32>324}325 326// -----327 328// CHECK-LABEL: func @extract_slice_linalg_readonly_use329func.func @extract_slice_linalg_readonly_use(330 %A : tensor<?x?xf32> {bufferization.writable = false},331 %B : tensor<4x4xf32> {bufferization.writable = false},332 %C : tensor<4x4xf32> {bufferization.writable = true})333 -> (tensor<4x4xf32>, tensor<4x4xf32>)334{335 // tensor.extract_slice is only used as a read, no interference irrespective336 // of user's inplace status.337 // CHECK: tensor.extract_slice338 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]}339 %sA = tensor.extract_slice %A[0, 0][4, 4][1, 1] : tensor<?x?xf32> to tensor<4x4xf32>340 341 // matmul output operand is not inplaceable at the function boundary.342 // CHECK: linalg.matmul343 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "false"]}344 %D = linalg.matmul ins(%sA, %B: tensor<4x4xf32>, tensor<4x4xf32>)345 outs(%B: tensor<4x4xf32>)346 -> tensor<4x4xf32>347 348 // matmul output operand is inplaceable at the function boundary.349 // CHECK: linalg.matmul350 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "true"]}351 %E = linalg.matmul ins(%sA, %B: tensor<4x4xf32>, tensor<4x4xf32>)352 outs(%C: tensor<4x4xf32>)353 -> tensor<4x4xf32>354 355 // CHECK: return356 // CHECK-SAME: __equivalent_func_args__ = [-1, 2]357 return %D, %E: tensor<4x4xf32>, tensor<4x4xf32>358}359 360// -----361 362// CHECK-LABEL: func @extract_slice_to_linalg_write_use363func.func @extract_slice_to_linalg_write_use(364 %A : tensor<4x4xf32> {bufferization.writable = false},365 %B : tensor<?x?xf32> {bufferization.writable = false},366 %C : tensor<?x?xf32> {bufferization.writable = true})367 -> (tensor<4x4xf32>, tensor<4x4xf32>)368{369 // Step 4. %sB forward propagates to a write in %D but it is not inplace.370 // So this is only ever read and can bufferize inplace.371 // CHECK: tensor.extract_slice372 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]}373 %sB = tensor.extract_slice %B[0, 0][4, 4][1, 1] : tensor<?x?xf32> to tensor<4x4xf32>374 375 // Step 3. %sB has a read interference in %E, it does not bufferize inplace.376 // CHECK: linalg.matmul377 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "false"]}378 %D = linalg.matmul ins(%B, %C: tensor<?x?xf32>, tensor<?x?xf32>)379 outs(%sB: tensor<4x4xf32>)380 -> tensor<4x4xf32>381 382 // Step 2. %sC forward propagates to an inplace write in %E.383 // %sC backward propagates to %C which is inplaceable.384 // As a consequence this is bufferized inplace.385 // CHECK: tensor.extract_slice386 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]}387 %sC = tensor.extract_slice %C[0, 0][4, 4][1, 1] : tensor<?x?xf32> to tensor<4x4xf32>388 389 // Step 1. %sC backprops to the tensor.extract_slice producer which is not390 // considered an interference. This bufferizes inplace.391 // CHECK: linalg.matmul392 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "true"]}393 %E = linalg.matmul ins(%A, %sB: tensor<4x4xf32>, tensor<4x4xf32>)394 outs(%sC: tensor<4x4xf32>)395 -> tensor<4x4xf32>396 397 return %D, %E: tensor<4x4xf32>, tensor<4x4xf32>398}399 400// -----401 402// CHECK-LABEL: func @insert_slice_double_extract_slice403func.func @insert_slice_double_extract_slice(404 %s1: index,405 %s2: index,406 %s3: index,407 %s4: index,408 %A: tensor<8x6xf32> {bufferization.writable = false},409 %B: tensor<6x6xf32> {bufferization.writable = false},410 %C: tensor<30x20xf32> {bufferization.writable = true})411 -> tensor<30x20xf32>412{413 // CHECK: tensor.extract_slice414 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "none", "none", "none", "none"]}415 %15 = tensor.extract_slice %C[%s3, %s4] [%s1, %s2] [1, 1] : tensor<30x20xf32> to tensor<?x?xf32>416 417 // CHECK: linalg.matmul418 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "true"]}419 %18 = linalg.matmul ins(%A, %B : tensor<8x6xf32>, tensor<6x6xf32>) outs(%15 : tensor<?x?xf32>) -> tensor<?x?xf32>420 421 // CHECK: tensor.extract_slice422 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "none", "none"]}423 %19 = tensor.extract_slice %18[0, 0] [%s1, %s2] [1, 1] : tensor<?x?xf32> to tensor<?x?xf32>424 425 // CHECK: tensor.insert_slice426 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "none", "none", "none", "none"]}427 %20 = tensor.insert_slice %19 into %C[%s3, %s4] [%s1, %s2] [1, 1] : tensor<?x?xf32> into tensor<30x20xf32>428 429 // CHECK: return430 // CHECK-SAME: __equivalent_func_args__ = [6]431 return %20 : tensor<30x20xf32>432}433 434//===----------------------------------------------------------------------===//435// Transitive cases436//===----------------------------------------------------------------------===//437 438// -----439 440// CHECK-LABEL: func @extract_slice_to_linalg_write_use441func.func @extract_slice_to_linalg_write_use(442 %A : tensor<4x4xf32> {bufferization.writable = false},443 %B : tensor<?x?xf32> {bufferization.writable = false},444 %C : tensor<?x?xf32> {bufferization.writable = true})445 -> (tensor<4x4xf32>, tensor<4x4xf32>)446{447 // Step 4. %sB forward propagates to an inplace write in %D.448 // %sB backward propagates to %B which is not inplaceable.449 // As a consequence this is bufferized out of place.450 // CHECK: tensor.extract_slice451 // CHECK-SAME: {__inplace_operands_attr__ = ["false"]}452 %sB = tensor.extract_slice %B[0, 0][4, 4][1, 1] : tensor<?x?xf32> to tensor<4x4xf32>453 454 // Step 3. %sB backprops to the tensor.extract_slice producer which is not455 // considered an interference. This bufferizes inplace.456 // CHECK: linalg.matmul457 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "true"]}458 %D = linalg.matmul ins(%B, %C: tensor<?x?xf32>, tensor<?x?xf32>)459 outs(%sB: tensor<4x4xf32>)460 -> tensor<4x4xf32>461 462 // Step 2. %sC forward propagates to an inplace write in %E.463 // %sC backward propagates to %C which is inplaceable.464 // As a consequence this is bufferized inplace.465 // CHECK: tensor.extract_slice466 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]}467 %sC = tensor.extract_slice %C[0, 0][4, 4][1, 1] : tensor<?x?xf32> to tensor<4x4xf32>468 469 // Step 1. %sC backprops to the tensor.extract_slice producer which is not470 // considered an interference. This bufferizes inplace.471 // CHECK: linalg.matmul472 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "true"]}473 %E = linalg.matmul ins(%A, %A: tensor<4x4xf32>, tensor<4x4xf32>)474 outs(%sC: tensor<4x4xf32>)475 -> tensor<4x4xf32>476 477 return %D, %E: tensor<4x4xf32>, tensor<4x4xf32>478}479 480// -----481 482// CHECK-LABEL: func @nested_extract_slice_and_insert483func.func @nested_extract_slice_and_insert(484 %A : tensor<?x?xf32> {bufferization.writable = false},485 %B : tensor<?x?xf32> {bufferization.writable = true},486 %C : tensor<?x?xf32> {bufferization.writable = true},487 %idx : index,488 %sz1 : index,489 %sz2 : index)490 -> (tensor<?x?xf32>, tensor<?x?xf32>, tensor<?x?xf32>)491{492 %f0 = arith.constant 0.0 : f32493 494 // 2-level matching tensor.extract_slice / tensor.insert_slice into non495 // inplaceable %A.496 // - %rA is not inplaceable because %A is not inplaceable at function boundary.497 // - once %rA is deemed not inplaceable, nothing prevent %rsA to be inplaceable498 // - this propagates to %FA and %ssA being inplaceable.499 // - %sA would then bufferize to an inplace write (i.e. %FA) but %A is not500 // inplaceable and so %sA is not inplaceable.501 // CHECK: tensor.extract_slice502 // CHECK-SAME: {__inplace_operands_attr__ = ["false", "none", "none"]}503 // CHECK-NEXT: tensor.extract_slice504 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]}505 // CHECK-NEXT: fill506 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true"]}507 // CHECK-NEXT: tensor.insert_slice508 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true"]}509 // CHECK-NEXT: tensor.insert_slice510 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "false", "none", "none"]}511 %sA = tensor.extract_slice %A[0, 0][%idx, %idx][1, 1] : tensor<?x?xf32> to tensor<?x?xf32>512 %ssA = tensor.extract_slice %sA[0, 0][4, 4][1, 1] : tensor<?x?xf32> to tensor<4x4xf32>513 %FA = linalg.fill ins(%f0 : f32) outs(%ssA : tensor<4x4xf32>) -> tensor<4x4xf32>514 %rsA = tensor.insert_slice %FA into %sA[0, 0][4, 4][1, 1] : tensor<4x4xf32> into tensor<?x?xf32>515 %rA = tensor.insert_slice %rsA into %A[0, 0][%idx, %idx][1, 1] : tensor<?x?xf32> into tensor<?x?xf32>516 517 // 3-level matching tensor.extract_slice / tensor.insert_slice into518 // inplaceable %B.519 // CHECK-NEXT: tensor.extract_slice520 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "none", "none"]}521 // CHECK-NEXT: tensor.extract_slice522 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "none"]}523 // CHECK-NEXT: tensor.extract_slice524 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]}525 // CHECK-NEXT: fill526 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true"]}527 // CHECK-NEXT: tensor.insert_slice528 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true"]}529 // CHECK-NEXT: tensor.insert_slice530 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "none"]}531 // CHECK-NEXT: tensor.insert_slice532 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "none", "none"]}533 %sB = tensor.extract_slice %B[0, 0][%idx, %idx][1, 1] : tensor<?x?xf32> to tensor<?x?xf32>534 %ssB = tensor.extract_slice %sB[0, 0][4, %idx][1, 1] : tensor<?x?xf32> to tensor<4x?xf32>535 %sssB = tensor.extract_slice %ssB[0, 0][4, 4][1, 1] : tensor<4x?xf32> to tensor<4x4xf32>536 %FB = linalg.fill ins(%f0 : f32) outs(%sssB : tensor<4x4xf32>) -> tensor<4x4xf32>537 %rssB = tensor.insert_slice %FB into %ssB[0, 0][4, 4][1, 1] : tensor<4x4xf32> into tensor<4x?xf32>538 %rsB = tensor.insert_slice %rssB into %sB[0, 0][4, %idx][1, 1] : tensor<4x?xf32> into tensor<?x?xf32>539 %rB = tensor.insert_slice %rsB into %B[0, 0][%idx, %idx][1, 1] : tensor<?x?xf32> into tensor<?x?xf32>540 541 // 2-level matching tensor.extract_slice / tensor.insert_slice into542 // inplaceable %C with a twist.543 // Throw a wrench in the system: %rsC production sizes do not match %ssC.544 // CHECK-NEXT: tensor.extract_slice545 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "none", "none"]}546 // The tensor.insert_slice that would be candidate for matching does not actually547 // match. That tensor.insert_slice can still be bufferized inplace nonetheless548 // but this tensor.extract_slice, which bufferizes to an inplace write, cannot.549 // CHECK-NEXT: tensor.extract_slice550 // CHECK-SAME: {__inplace_operands_attr__ = ["false", "none"]}551 // CHECK-NEXT: fill552 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true"]}553 // CHECK-NEXT: tensor.insert_slice554 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "none"]}555 // CHECK-NEXT: tensor.insert_slice556 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "none", "none"]}557 %sC = tensor.extract_slice %C[0, 0][%idx, %idx][1, 1] : tensor<?x?xf32> to tensor<?x?xf32>558 %ssC = tensor.extract_slice %sC[0, 0][%sz1, 4][1, 1] : tensor<?x?xf32> to tensor<?x4xf32>559 %FC = linalg.fill ins(%f0 : f32) outs(%ssC : tensor<?x4xf32>) -> tensor<?x4xf32>560 %rsC = tensor.insert_slice %FC into %sC[0, 0][%sz2, 4][1, 1] : tensor<?x4xf32> into tensor<?x?xf32>561 %rC = tensor.insert_slice %rsC into %C[0, 0][%idx, %idx][1, 1] : tensor<?x?xf32> into tensor<?x?xf32>562 563 // CHECK: return564 // CHECK-SAME: __equivalent_func_args__ = [-1, 1, 2]565 return %rA, %rB, %rC: tensor<?x?xf32>, tensor<?x?xf32>, tensor<?x?xf32>566}567 568// -----569 570//===----------------------------------------------------------------------===//571// Cross function boundary cases.572//===----------------------------------------------------------------------===//573 574func.func private @foo(tensor<64xf32>)575 576// CHECK-LABEL: dependence_through_call577func.func @dependence_through_call(%I : tensor<64xf32> {bufferization.writable = true}) {578 %f1 = arith.constant 1.000000e+00 : f32579 %f2 = arith.constant 2.000000e+00 : f32580 581 // 2. %B already bufferizes inplace, %A would alias and have a different582 // value. The calls to `foo` are determined to read conservatively, so %A583 // cannot bufferize inplace.584 // CHECK: fill585 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "false"]}586 %A = linalg.fill ins(%f1 : f32) outs(%I : tensor<64xf32>) -> tensor<64xf32>587 588 // 1. Bufferizes inplace: no alias to %A is yet possible.589 // CHECK: fill590 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true"]}591 %B = linalg.fill ins(%f2 : f32) outs(%I : tensor<64xf32>) -> tensor<64xf32>592 593 call @foo(%A) : (tensor<64xf32>) -> ()594 call @foo(%B) : (tensor<64xf32>) -> ()595 596 return597}598 599// -----600 601func.func private @foo(tensor<64xf32>)602 603func.func private @bar(%A : tensor<64xf32>) {604 call @foo(%A) : (tensor<64xf32>) -> ()605 return606}607 608func.func @read_dependence_through_scf_and_call(609 %I : tensor<64xf32> {bufferization.writable = true},610 %I2 : tensor<64xf32> {bufferization.writable = true}) {611 %c0 = arith.constant 0 : index612 %c1 = arith.constant 1 : index613 %c10 = arith.constant 10 : index614 %f1 = arith.constant 1.000000e+00 : f32615 %f2 = arith.constant 2.000000e+00 : f32616 617 // 5. %B bufferizes inplace, %A would alias and have a different value.618 // The calls to `foo` are determined to read conservatively, so %A cannot619 // bufferize inplace.620 // CHECK: fill621 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "false"]}622 %A = linalg.fill ins(%f1 : f32) outs(%I : tensor<64xf32>) -> tensor<64xf32>623 624 // 4. Bufferizes inplace: no alias to %A is yet possible.625 // CHECK: fill626 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true"]}627 %B = linalg.fill ins(%f2 : f32) outs(%I : tensor<64xf32>) -> tensor<64xf32>628 629 // 3. Does not read or write, bufferizes inplace.630 // CHECK: scf.for631 // CHECK-NEXT: scf.yield632 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true"]}633 // CHECK: } {__inplace_operands_attr__ = ["none", "none", "none", "true", "true"]}634 %r:2 = scf.for %i = %c0 to %c10 step %c1 iter_args(%0 = %A, %1 = %B)635 -> (tensor<64xf32>, tensor<64xf32>)636 {637 scf.yield %0, %1 : tensor<64xf32>, tensor<64xf32>638 }639 call @foo(%r#0) : (tensor<64xf32>) -> ()640 call @foo(%r#1) : (tensor<64xf32>) -> ()641 642 // 2. %B2 already bufferizes inplace, %A2 would alias and have a different643 // value. The calls to `foo` are determined to read conservatively, so %A2644 // cannot bufferize inplace.645 // CHECK: fill646 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "false"]}647 %A2 = linalg.fill ins(%f1 : f32) outs(%I2 : tensor<64xf32>) -> tensor<64xf32>648 649 // 1. Bufferizes inplace: no alias to %A2 is yet possible.650 // CHECK: fill651 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true"]}652 %B2 = linalg.fill ins(%f2 : f32) outs(%I2 : tensor<64xf32>) -> tensor<64xf32>653 654 call @bar(%A2) : (tensor<64xf32>) -> ()655 call @bar(%B2) : (tensor<64xf32>) -> ()656 return657}658 659// -----660 661//===----------------------------------------------------------------------===//662// Transitive cases through extract_slice.663//===----------------------------------------------------------------------===//664 665// CHECK-LABEL: func @write_into_constant_via_alias666func.func @write_into_constant_via_alias(%v : vector<5xi32>,667 %s1 : index, %s2 : index,668 %s3 : index) -> tensor<?xi32> {669 %A = arith.constant dense<[1, 2, 3, 4]> : tensor<4xi32>670 // CHECK: tensor.extract_slice671 // CHECK-SAME: {__inplace_operands_attr__ = ["false", "none", "none"]}672 %b = tensor.extract_slice %A[%s1][%s2][1] : tensor<4xi32> to tensor<?xi32>673 // CHECK: vector.transfer_write674 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true", "none"]}675 %r = vector.transfer_write %v, %b[%s3] : vector<5xi32>, tensor<?xi32>676 return %r : tensor<?xi32>677}678 679// -----680 681func.func @matmul_on_tensors(682 %arg0: tensor<518x518xf32> {bufferization.buffer_layout = affine_map<(d0, d1) -> (d0, d1)>, bufferization.writable = false},683 %arg1: tensor<518x518xf32> {bufferization.buffer_layout = affine_map<(d0, d1) -> (d0, d1)>, bufferization.writable = false},684 %arg2: tensor<256x256xf32> {bufferization.buffer_layout = affine_map<(d0, d1) -> (d0, d1)>, bufferization.writable = true})685 -> tensor<256x256xf32>686{687 %c0 = arith.constant 0 : index688 %cst_0 = arith.constant 0.000000e+00 : f32689 %cst_1 = arith.constant 1.000000e+00 : f32690 691 %7 = bufferization.alloc_tensor() : tensor<256x256xf32>692 693 // CHECK: linalg.fill694 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "false"]}695 // CHECK: linalg.fill696 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true"]}697 %8 = linalg.fill ins(%cst_0 : f32) outs(%7 : tensor<256x256xf32>) -> tensor<256x256xf32>698 %11 = linalg.fill ins(%cst_1 : f32) outs(%7 : tensor<256x256xf32>) -> tensor<256x256xf32>699 700 // CHECK: tensor.extract_slice701 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]}702 // CHECK: tensor.extract_slice703 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]}704 // CHECK: linalg.matmul705 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "true"]}706 %sA = tensor.extract_slice %8[0, 0][256, 16][1, 1]: tensor<256x256xf32> to tensor<256x16xf32>707 %sB = tensor.extract_slice %11[0, 0][16, 256][1, 1]: tensor<256x256xf32> to tensor<16x256xf32>708 %r = linalg.matmul709 ins(%sA, %sB : tensor<256x16xf32>, tensor<16x256xf32>)710 outs(%arg2 : tensor<256x256xf32>) -> tensor<256x256xf32>711 712 // CHECK: return713 // CHECK-SAME: __equivalent_func_args__ = [2]714 return %r : tensor<256x256xf32>715}716 717// -----718 719func.func @matmul_on_tensors(720 %arg0: tensor<518x518xf32> {bufferization.buffer_layout = affine_map<(d0, d1) -> (d0, d1)>, bufferization.writable = false},721 %arg1: tensor<518x518xf32> {bufferization.buffer_layout = affine_map<(d0, d1) -> (d0, d1)>, bufferization.writable = false},722 %arg2: tensor<256x256xf32> {bufferization.buffer_layout = affine_map<(d0, d1) -> (d0, d1)>, bufferization.writable = true})723 -> tensor<256x256xf32>724{725 %c0 = arith.constant 0 : index726 %cst_0 = arith.constant 0.000000e+00 : f32727 %cst_1 = arith.constant 1.000000e+00 : f32728 729 %7 = bufferization.alloc_tensor() : tensor<256x256xf32>730 731 // CHECK: linalg.fill732 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "false"]}733 // CHECK: vector.transfer_write734 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true", "none", "none"]735 %8 = linalg.fill ins(%cst_0 : f32) outs(%7 : tensor<256x256xf32>) -> tensor<256x256xf32>736 %9 = vector.transfer_read %arg0[%c0, %c0], %cst_0 {in_bounds = [false, true]} : tensor<518x518xf32>, vector<256x256xf32>737 %10 = vector.transfer_write %9, %8[%c0, %c0] {in_bounds = [true, true]} : vector<256x256xf32>, tensor<256x256xf32>738 739 // CHECK: linalg.fill740 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true"]}741 // CHECK: vector.transfer_write742 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true", "none", "none"]743 %11 = linalg.fill ins(%cst_1 : f32) outs(%7 : tensor<256x256xf32>) -> tensor<256x256xf32>744 %12 = vector.transfer_read %arg1[%c0, %c0], %cst_0 {in_bounds = [false, true]} : tensor<518x518xf32>, vector<256x256xf32>745 %13 = vector.transfer_write %12, %11[%c0, %c0] {in_bounds = [true, true]} : vector<256x256xf32>, tensor<256x256xf32>746 747 // CHECK: tensor.extract_slice748 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]}749 // CHECK: tensor.extract_slice750 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]}751 // CHECK: linalg.matmul752 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "true"]}753 %sA = tensor.extract_slice %10[0, 0][256, 16][1, 1]: tensor<256x256xf32> to tensor<256x16xf32>754 %sB = tensor.extract_slice %13[0, 0][16, 256][1, 1]: tensor<256x256xf32> to tensor<16x256xf32>755 %r = linalg.matmul756 ins(%sA, %sB : tensor<256x16xf32>, tensor<16x256xf32>)757 outs(%arg2 : tensor<256x256xf32>) -> tensor<256x256xf32>758 759 // CHECK: return760 // CHECK-SAME: __equivalent_func_args__ = [2]761 return %r : tensor<256x256xf32>762}763 764// -----765 766//===----------------------------------------------------------------------===//767// Chain of tensor.insert_slice is better traversed in reverse order without768// prioritizing the tensor.insert_slice ops.769//===----------------------------------------------------------------------===//770 771// CHECK-LABEL: func @insert_slice_chain(772func.func @insert_slice_chain(773 %v1: vector<32x90xf32>,774 %v2: vector<30x90xf32>,775 %arg0: tensor<62x126xf32> {bufferization.buffer_layout = affine_map<(d0, d1) -> (d0, d1)>, bufferization.writable = false},776// CHECK-SAME: bufferization.access = "none"777 %arg1: tensor<126x90xf32> {bufferization.buffer_layout = affine_map<(d0, d1) -> (d0, d1)>, bufferization.writable = false},778// CHECK-SAME: bufferization.access = "none"779 %arg2: tensor<62x90xf32> {bufferization.buffer_layout = affine_map<(d0, d1) -> (d0, d1)>, bufferization.writable = true})780// CHECK-SAME: bufferization.access = "write"781 -> tensor<62x90xf32> attributes {passthrough = [["prefer-vector-width", "512"]], target_cpu = "skylake-avx512"}782{783 %c0 = arith.constant 0 : index784 %cst = arith.constant 0.000000e+00 : f32785 786 // CHECK: linalg.fill787 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true"]788 %0 = linalg.fill ins(%cst : f32) outs(%arg2 : tensor<62x90xf32>) -> tensor<62x90xf32>789 790 // CHECK: tensor.extract_slice791 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]792 %2 = tensor.extract_slice %0[0, 0] [32, 90] [1, 1] : tensor<62x90xf32> to tensor<32x90xf32>793 // CHECK: vector.transfer_write794 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true", "none", "none"]795 %7 = vector.transfer_write %v1, %2[%c0, %c0] {in_bounds = [true, true]} : vector<32x90xf32>, tensor<32x90xf32>796 // CHECK: tensor.insert_slice797 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true"]798 %8 = tensor.insert_slice %7 into %0[0, 0] [32, 90] [1, 1] : tensor<32x90xf32> into tensor<62x90xf32>799 800 // CHECK: tensor.extract_slice801 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]802 %10 = tensor.extract_slice %8[32, 0] [30, 90] [1, 1] : tensor<62x90xf32> to tensor<30x90xf32>803 // CHECK: vector.transfer_write804 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true", "none", "none"]805 %14 = vector.transfer_write %v2, %10[%c0, %c0] {in_bounds = [true, true]} : vector<30x90xf32>, tensor<30x90xf32>806 // CHECK: tensor.insert_slice807 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true"]808 %15 = tensor.insert_slice %14 into %8[32, 0] [30, 90] [1, 1] : tensor<30x90xf32> into tensor<62x90xf32>809 810 // CHECK: return811 // CHECK-SAME: __equivalent_func_args__ = [4]812 return %15 : tensor<62x90xf32>813}814 815// -----816 817//===----------------------------------------------------------------------===//818// Insert point issue cases.819//===----------------------------------------------------------------------===//820 821// Only test IR validity wrt dominance.822// CHECK-LABEL: func @ip823func.func @ip(%t: tensor<10x20xf32> {bufferization.writable = true},824 %x: index, %y: index, %v: vector<5x6xf32>)825 -> tensor<10x20xf32>826{827 %c0 = arith.constant 0 : index828 %c256 = arith.constant 256 : index829 %c257 = arith.constant 257 : index830 %r = scf.for %arg0 = %c0 to %c257 step %c256 iter_args(%arg1 = %t) -> (tensor<10x20xf32>) {831 %t1 = tensor.extract_slice %arg1[%x, 0] [5, %y] [1, 1] : tensor<10x20xf32> to tensor<5x?xf32>832 %t11 = tensor.extract_slice %t1[0, 0] [5, %y] [1, 1] : tensor<5x?xf32> to tensor<5x?xf32>833 %t2 = vector.transfer_write %v, %t11[%c0, %c0] : vector<5x6xf32>, tensor<5x?xf32>834 %t3 = tensor.insert_slice %t2 into %arg1[%x, 0] [5, %y] [1, 1] : tensor<5x?xf32> into tensor<10x20xf32>835 scf.yield %t3 : tensor<10x20xf32>836 }837 838 // CHECK: return839 // CHECK-SAME: __equivalent_func_args__ = [0]840 return %r : tensor<10x20xf32>841}842 843// -----844 845#accesses = [846 affine_map<(i) -> (i)>,847 affine_map<(i) -> (i)>,848 affine_map<(i) -> (i)>849]850#trait = {851 indexing_maps = #accesses,852 iterator_types = ["parallel"]853}854 855// CHECK-LABEL: func @linalg_op_same_out_tensors(856func.func @linalg_op_same_out_tensors(857 %t1: tensor<?xf32> {bufferization.writable = true},858// CHECK-SAME: bufferization.access = "read"859 %t2: tensor<?xf32> {bufferization.writable = true})860// CHECK-SAME: bufferization.access = "write"861 -> (tensor<?xf32>, tensor<?xf32>){862 863 // CHECK: linalg.generic864 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "false"]865 %o:2 = linalg.generic #trait ins(%t1 : tensor<?xf32>)866 outs (%t2, %t2 : tensor<?xf32>, tensor<?xf32>) {867 ^bb(%0: f32, %1: f32, %2 : f32) :868 linalg.yield %0, %0 : f32, f32869 } -> (tensor<?xf32>, tensor<?xf32>)870 871 // CHECK: return872 // CHECK-SAME: __equivalent_func_args__ = [1, -1]873 return %o#0, %o#1 : tensor<?xf32>, tensor<?xf32>874}875 876// -----877 878#accesses = [879 affine_map<(i) -> (i)>,880 affine_map<(i) -> (i)>,881 affine_map<(i) -> (i)>,882 affine_map<(i) -> (i)>883]884#trait = {885 indexing_maps = #accesses,886 iterator_types = ["parallel"]887}888 889// CHECK-LABEL: func @linalg_op_same_out_tensors_2(890func.func @linalg_op_same_out_tensors_2(891 %t1: tensor<?xf32> {bufferization.writable = true},892// CHECK-SAME: bufferization.access = "read"893 %t2: tensor<?xf32> {bufferization.writable = true})894// CHECK-SAME: bufferization.access = "write"895 -> (tensor<?xf32>, tensor<?xf32>, tensor<?xf32>){896 897 // CHECK: linalg.generic898 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "false", "false"]899 %o:3 = linalg.generic #trait900 ins(%t1 : tensor<?xf32>)901 outs (%t2, %t2, %t2 : tensor<?xf32>, tensor<?xf32>, tensor<?xf32>) {902 ^bb(%0: f32, %1: f32, %2 : f32, %3 : f32) :903 linalg.yield %0, %0, %0 : f32, f32, f32904 } -> (tensor<?xf32>, tensor<?xf32>, tensor<?xf32>)905 906 // CHECK: return907 // CHECK-SAME: __equivalent_func_args__ = [1, -1, -1]908 return %o#0, %o#1, %o#2 : tensor<?xf32>, tensor<?xf32>, tensor<?xf32>909}910 911// -----912 913// CHECK-LABEL: func @double_insert_slice_into_alias914func.func @double_insert_slice_into_alias(915 %v1: vector<32x90xf32>,916 %v2: vector<30x90xf32>,917 %arg2: tensor<62x90xf32> {bufferization.writable = true},918 %s1: index, %s2: index, %s3: index, %s4: index)919 -> (tensor<62x90xf32>, tensor<?x?xf32>)920{921 %c0 = arith.constant 0 : index922 923 // Cannot bufferize inplace this extract_slice because both operand and result924 // are modified and returned separately.925 // CHECK: tensor.extract_slice926 // CHECK-SAME: {__inplace_operands_attr__ = ["false", "none", "none", "none", "none"]927 %e = tensor.extract_slice %arg2[%s1, %s2][%s3, %s4][1, 1] : tensor<62x90xf32> to tensor<?x?xf32>928 929 // CHECK: tensor.extract_slice930 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]931 %2 = tensor.extract_slice %arg2[0, 0] [32, 90] [1, 1] : tensor<62x90xf32> to tensor<32x90xf32>932 // CHECK: vector.transfer_write933 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true", "none", "none"]934 %7 = vector.transfer_write %v1, %2[%c0, %c0] {in_bounds = [true, true]} : vector<32x90xf32>, tensor<32x90xf32>935 // CHECK: tensor.insert_slice936 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true"]937 %8 = tensor.insert_slice %7 into %arg2[0, 0] [32, 90] [1, 1] : tensor<32x90xf32> into tensor<62x90xf32>938 939 // CHECK: tensor.extract_slice940 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]941 %10 = tensor.extract_slice %e[32, 0] [30, 90] [1, 1] : tensor<?x?xf32> to tensor<30x90xf32>942 // CHECK: vector.transfer_write943 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true", "none", "none"]944 %14 = vector.transfer_write %v2, %10[%c0, %c0] {in_bounds = [true, true]} : vector<30x90xf32>, tensor<30x90xf32>945 // CHECK: tensor.insert_slice946 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true"]947 %15 = tensor.insert_slice %14 into %e[32, 0] [30, 90] [1, 1] : tensor<30x90xf32> into tensor<?x?xf32>948 949 // CHECK: return950 // CHECK-SAME: __equivalent_func_args__ = [2, -1]951 return %8, %15 : tensor<62x90xf32>, tensor<?x?xf32>952}953 954// -----955 956// CHECK-LABEL: func @interleaved_extract_insert_slice_chain_1957func.func @interleaved_extract_insert_slice_chain_1(958 %arg2: tensor<62x90xf32> {bufferization.writable = true})959 -> (tensor<62x90xf32>)960{961 // CHECK: tensor.extract_slice962 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]963 %2 = tensor.extract_slice %arg2[0, 0] [32, 90] [1, 1] : tensor<62x90xf32> to tensor<32x90xf32>964 965 // TODO: This should bufferize inplace once we have a proper range analysis.966 // CHECK: tensor.extract_slice967 // CHECK-SAME: {__inplace_operands_attr__ = ["false"]968 %10 = tensor.extract_slice %arg2[32, 0] [30, 90] [1, 1] : tensor<62x90xf32> to tensor<30x90xf32>969 970 971 // CHECK: tensor.insert_slice972 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true"]973 %8 = tensor.insert_slice %2 into %arg2[0, 0] [32, 90] [1, 1] : tensor<32x90xf32> into tensor<62x90xf32>974 975 976 // CHECK: tensor.insert_slice977 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true"]978 %15 = tensor.insert_slice %10 into %8[32, 0] [30, 90] [1, 1] : tensor<30x90xf32> into tensor<62x90xf32>979 980 // CHECK: return981 // CHECK-SAME: __equivalent_func_args__ = [0]982 return %15 : tensor<62x90xf32>983}984 985// -----986 987// CHECK-LABEL: func @interleaved_extract_insert_slice_chain_2988func.func @interleaved_extract_insert_slice_chain_2(989 %arg2: tensor<62x90xf32> {bufferization.writable = true})990 -> (tensor<62x90xf32>)991{992 // CHECK: tensor.extract_slice993 // CHECK-SAME: {__inplace_operands_attr__ = ["true"]994 %2 = tensor.extract_slice %arg2[0, 0] [32, 90] [1, 1] : tensor<62x90xf32> to tensor<32x90xf32>995 996 // The slices are overlapping, so this can never bufferize inplace.997 // CHECK: tensor.extract_slice998 // CHECK-SAME: {__inplace_operands_attr__ = ["false"]999 %10 = tensor.extract_slice %arg2[31, 0] [30, 90] [1, 1] : tensor<62x90xf32> to tensor<30x90xf32>1000 1001 1002 // CHECK: tensor.insert_slice1003 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true"]1004 %8 = tensor.insert_slice %2 into %arg2[0, 0] [32, 90] [1, 1] : tensor<32x90xf32> into tensor<62x90xf32>1005 1006 1007 // CHECK: tensor.insert_slice1008 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true"]1009 %15 = tensor.insert_slice %10 into %8[31, 0] [30, 90] [1, 1] : tensor<30x90xf32> into tensor<62x90xf32>1010 1011 // CHECK: return1012 // CHECK-SAME: __equivalent_func_args__ = [0]1013 return %15 : tensor<62x90xf32>1014}1015 1016// -----1017 1018// CHECK-LABEL: func @extract_once_insert_twice1019func.func @extract_once_insert_twice(1020 %arg2: tensor<62x90xf32> {bufferization.writable = true})1021 -> (tensor<62x90xf32>)1022{1023 // CHECK: tensor.extract_slice1024 // CHECK-SAME: {__inplace_operands_attr__ = ["false"]1025 %2 = tensor.extract_slice %arg2[0, 0] [32, 90] [1, 1] : tensor<62x90xf32> to tensor<32x90xf32>1026 1027 // CHECK: tensor.insert_slice1028 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true"]1029 %8 = tensor.insert_slice %2 into %arg2[0, 0] [32, 90] [1, 1] : tensor<32x90xf32> into tensor<62x90xf32>1030 1031 // CHECK: tensor.insert_slice1032 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true"]1033 %15 = tensor.insert_slice %2 into %8[15, 0] [32, 90] [1, 1] : tensor<32x90xf32> into tensor<62x90xf32>1034 1035 // CHECK: return1036 // CHECK-SAME: __equivalent_func_args__ = [0]1037 return %15 : tensor<62x90xf32>1038}1039 1040// -----1041 1042// CHECK-LABEL: func @some_use1043func.func @some_use(%A : tensor<?xf32> {bufferization.writable = true},1044 %v : vector<5xf32>) -> (tensor<?xf32>) {1045 %idx = arith.constant 0 : index1046 // CHECK: vector.transfer_write1047 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true", "none"]1048 %0 = vector.transfer_write %v, %A[%idx] : vector<5xf32>, tensor<?xf32>1049 return %0 : tensor<?xf32>1050}1051 1052 1053// CHECK-LABEL: func @main_func1054func.func @main_func(%A : tensor<?xf32> {bufferization.writable = true},1055 %v : vector<5xf32>) -> (tensor<?xf32>) {1056 // CHECK: call1057 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "none"]1058 %0 = call @some_use(%A, %v) : (tensor<?xf32>, vector<5xf32>) -> (tensor<?xf32>)1059 return %0 : tensor<?xf32>1060}1061 1062// -----1063 1064// CHECK-LABEL: func @to_tensor_op_not_writable1065func.func @to_tensor_op_not_writable(%m: memref<?xf32>, %v: vector<5xf32>,1066 %idx1: index, %idx2: index)1067 -> vector<10xf32> {1068 %0 = bufferization.to_tensor %m restrict : memref<?xf32> to tensor<?xf32>1069 1070 // Write to the tensor. Cannot be inplace due to tensor_load.1071 // CHECK: vector.transfer_write1072 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "false", "none"]1073 %w = vector.transfer_write %v, %0[%idx1] : vector<5xf32>, tensor<?xf32>1074 1075 // Read from the tensor and return result.1076 %cst = arith.constant 0.0 : f321077 %r = vector.transfer_read %w[%idx2], %cst : tensor<?xf32>, vector<10xf32>1078 return %r : vector<10xf32>1079}1080 1081// -----1082 1083// CHECK-LABEL: func @inner_func1084func.func @inner_func(%t: tensor<?xf32>) -> tensor<?xf32> {1085 // CHECK: return1086 // CHECK-SAME: __equivalent_func_args__ = [0]1087 return %t : tensor<?xf32>1088}1089 1090func.func @equivalent_func_arg(%c0: index, %c10: index, %c1: index, %t0: tensor<?xf32>) -> tensor<?xf32> {1091 // This test does not check IR. It just asserts there is no failure due to1092 // non-equivalent scf.for yield values.1093 %1 = scf.for %iv = %c0 to %c10 step %c1 iter_args(%t1 = %t0) -> (tensor<?xf32>) {1094 %3 = func.call @inner_func(%t1) : (tensor<?xf32>) -> tensor<?xf32>1095 scf.yield %3 : tensor<?xf32>1096 }1097 return %1: tensor<?xf32>1098}1099 1100// -----1101 1102// CHECK-LABEL: func @inner_func_21103func.func @inner_func_2(%t: tensor<?xf32>) -> tensor<?xf32> {1104 %f = arith.constant 1.0 : f321105 %c0 = arith.constant 0 : index1106 %0 = tensor.insert %f into %t[%c0] : tensor<?xf32>1107 // CHECK: return1108 // CHECK-SAME: __equivalent_func_args__ = [0]1109 return %0 : tensor<?xf32>1110}1111 1112func.func @equivalent_func_arg_2(%c0: index, %c10: index, %c1: index, %t0: tensor<?xf32>) -> tensor<?xf32> {1113 // This test does not check IR. It just asserts there is no failure due to1114 // non-equivalent scf.for yield values.1115 %1 = scf.for %iv = %c0 to %c10 step %c1 iter_args(%t1 = %t0) -> (tensor<?xf32>) {1116 %3 = func.call @inner_func_2(%t1) : (tensor<?xf32>) -> tensor<?xf32>1117 scf.yield %3 : tensor<?xf32>1118 }1119 return %1: tensor<?xf32>1120}1121 1122// -----1123 1124// CHECK-LABEL: func @write_after_select_read_one1125// CHECK-SAME: %[[t1:.*]]: tensor<?xf32> {{.*}}, %[[t2:.*]]: tensor<?xf32>1126func.func @write_after_select_read_one(1127 %t1 : tensor<?xf32> {bufferization.writable = true},1128 %t2 : tensor<?xf32> {bufferization.writable = true},1129 %c : i1)1130 -> (f32, tensor<?xf32>)1131{1132 %cst = arith.constant 0.0 : f321133 %idx = arith.constant 0 : index1134 1135 // CHECK: arith.select %{{.*}}, %[[t1]], %[[t2]]1136 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "false", "true"]}1137 %s = arith.select %c, %t1, %t2 : tensor<?xf32>1138 // CHECK: tensor.insert1139 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true", "none"]}1140 %w = tensor.insert %cst into %s[%idx] : tensor<?xf32>1141 // CHECK: tensor.extract1142 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "none"]}1143 %f = tensor.extract %t1[%idx] : tensor<?xf32>1144 1145 return %f, %w : f32, tensor<?xf32>1146}1147 1148// -----1149 1150// CHECK-LABEL: func @write_after_select_read_both1151// CHECK-SAME: %[[t1:.*]]: tensor<?xf32> {{.*}}, %[[t2:.*]]: tensor<?xf32>1152func.func @write_after_select_read_both(1153 %t1 : tensor<?xf32> {bufferization.writable = true},1154 %t2 : tensor<?xf32> {bufferization.writable = true},1155 %c : i1)1156 -> (f32, f32, tensor<?xf32>)1157{1158 %cst = arith.constant 0.0 : f321159 %idx = arith.constant 0 : index1160 1161 // CHECK: arith.select %{{.*}}, %[[t1]], %[[t2]]1162 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "false", "false"]}1163 %s = arith.select %c, %t1, %t2 : tensor<?xf32>1164 // CHECK: tensor.insert1165 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true", "none"]}1166 %w = tensor.insert %cst into %s[%idx] : tensor<?xf32>1167 // CHECK: tensor.extract1168 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "none"]}1169 %f = tensor.extract %t1[%idx] : tensor<?xf32>1170 // CHECK: tensor.extract1171 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "none"]}1172 %f2 = tensor.extract %t2[%idx] : tensor<?xf32>1173 1174 return %f, %f2, %w : f32, f32, tensor<?xf32>1175}1176 1177// -----1178 1179// CHECK-LABEL: func @write_after_select_no_conflict1180// CHECK-SAME: %[[t1:.*]]: tensor<?xf32> {{.*}}, %[[t2:.*]]: tensor<?xf32>1181func.func @write_after_select_no_conflict(1182 %t1 : tensor<?xf32> {bufferization.writable = true},1183 %t2 : tensor<?xf32> {bufferization.writable = true},1184 %c : i1)1185 -> (f32, tensor<?xf32>)1186{1187 %cst = arith.constant 0.0 : f321188 %idx = arith.constant 0 : index1189 1190 // CHECK: arith.select %{{.*}}, %[[t1]], %[[t2]]1191 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true", "true"]}1192 %s = arith.select %c, %t1, %t2 : tensor<?xf32>1193 // CHECK: tensor.insert1194 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true", "none"]}1195 %w = tensor.insert %cst into %s[%idx] : tensor<?xf32>1196 // CHECK: tensor.extract1197 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "none"]}1198 %f = tensor.extract %w[%idx] : tensor<?xf32>1199 1200 return %f, %w : f32, tensor<?xf32>1201}1202 1203// -----1204 1205// CHECK-LABEL: func @write_to_same_tensor_in_loop_out_of_place(1206func.func @write_to_same_tensor_in_loop_out_of_place(1207 %A : tensor<?xf32> {bufferization.writable = true},1208 %B : tensor<?xf32> {bufferization.writable = true},1209 %lb : index, %ub : index, %step : index, %sz: index)1210 -> (tensor<?xf32>)1211{1212 // CHECK: scf.for {{.*}} {1213 %r0 = scf.for %i = %lb to %ub step %step iter_args(%t = %A) -> (tensor<?xf32>) {1214 %i2 = arith.index_cast %i : index to i321215 %i3 = arith.sitofp %i2 : i32 to f321216 // The tensor.insert is out-of-place because the %B is written multiple1217 // times inside a loop.1218 // CHECK: tensor.insert1219 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "false", "none"]}1220 %B2 = tensor.insert %i3 into %B[%i] : tensor<?xf32>1221 // CHECK: tensor.insert_slice1222 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "none", "none"]}1223 %A2 = tensor.insert_slice %B2 into %t[%i][%sz][1] : tensor<?xf32> into tensor<?xf32>1224 scf.yield %A2 : tensor<?xf32>1225 }1226 // CHECK: } {__inplace_operands_attr__ = ["none", "none", "none", "true"]}1227 1228 return %r0 : tensor<?xf32>1229}1230 1231// -----1232 1233// CHECK-LABEL: func @write_to_same_alloc_tensor_in_place(1234func.func @write_to_same_alloc_tensor_in_place(1235 %A : tensor<?xf32> {bufferization.writable = true},1236 %lb : index, %ub : index, %step : index, %sz: index, %sz2: index)1237 -> (tensor<?xf32>)1238{1239 %B = bufferization.alloc_tensor(%sz2) : tensor<?xf32>1240 1241 // CHECK: scf.for {{.*}} {1242 %r0 = scf.for %i = %lb to %ub step %step iter_args(%t = %A) -> (tensor<?xf32>) {1243 %i2 = arith.index_cast %i : index to i321244 %i3 = arith.sitofp %i2 : i32 to f321245 // %B is written multiple times inside a loop, but it is an alloc_tensor.1246 // CHECK: tensor.insert1247 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "true", "none"]}1248 %B2 = tensor.insert %i3 into %B[%i] : tensor<?xf32>1249 // CHECK: tensor.insert_slice1250 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "none", "none"]}1251 %A2 = tensor.insert_slice %B2 into %t[%i][%sz][1] : tensor<?xf32> into tensor<?xf32>1252 scf.yield %A2 : tensor<?xf32>1253 }1254 // CHECK: } {__inplace_operands_attr__ = ["none", "none", "none", "true"]}1255 1256 return %r0 : tensor<?xf32>1257}1258 1259// -----1260 1261// CHECK-LABEL: func @write_to_same_alloc_tensor_out_of_place(1262func.func @write_to_same_alloc_tensor_out_of_place(1263 %A : tensor<?xf32> {bufferization.writable = true},1264 %lb : index, %ub : index, %step : index, %sz: index, %sz2: index, %f: f32)1265 -> (tensor<?xf32>)1266{1267 %B = bufferization.alloc_tensor(%sz2) : tensor<?xf32>1268 %C = tensor.insert %f into %B[%lb] : tensor<?xf32>1269 1270 // CHECK: scf.for {{.*}} {1271 %r0 = scf.for %i = %lb to %ub step %step iter_args(%t = %A) -> (tensor<?xf32>) {1272 %i2 = arith.index_cast %i : index to i321273 %i3 = arith.sitofp %i2 : i32 to f321274 // %C is written multiple times inside a loop. Even though %C aliases with1275 // an alloc_tensor, out-of-bounds bufferization is necessary because there1276 // is another alias (%C) outside of the loop.1277 // CHECK: tensor.insert1278 // CHECK-SAME: {__inplace_operands_attr__ = ["none", "false", "none"]}1279 %B2 = tensor.insert %i3 into %C[%i] : tensor<?xf32>1280 // CHECK: tensor.insert_slice1281 // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "none", "none"]}1282 %A2 = tensor.insert_slice %B2 into %t[%i][%sz][1] : tensor<?xf32> into tensor<?xf32>1283 scf.yield %A2 : tensor<?xf32>1284 }1285 // CHECK: } {__inplace_operands_attr__ = ["none", "none", "none", "true"]}1286 1287 return %r0 : tensor<?xf32>1288}1289 1290// -----1291 1292// CHECK-LABEL: func.func private @ext_func(tensor<?xf32> {bufferization.access = "read-write"})1293func.func private @ext_func(%t: tensor<?xf32>)1294 1295// CHECK: func.func @private_func_read_write(%{{.*}}: tensor<5xf32> {bufferization.access = "read"})1296func.func @private_func_read_write(%t: tensor<5xf32>) -> f32 {1297 %c0 = arith.constant 0 : index1298 // Bufferizes out-of-place because `ext_func` may modify the buffer.1299 // CHECK: tensor.cast {{.*}} {__inplace_operands_attr__ = ["false"]}1300 %0 = tensor.cast %t : tensor<5xf32> to tensor<?xf32>1301 func.call @ext_func(%0) : (tensor<?xf32>) -> ()1302 %1 = tensor.extract %t[%c0] : tensor<5xf32>1303 return %1 : f321304}1305 1306// -----1307 1308// CHECK-LABEL: func.func private @print_buffer(tensor<*xf32> {bufferization.access = "read"})1309func.func private @print_buffer(%t: tensor<*xf32> {bufferization.access = "read"})1310 1311// CHECK: func.func @private_func_read(%{{.*}}: tensor<5xf32> {bufferization.access = "read"})1312func.func @private_func_read(%t: tensor<5xf32>) -> f32 {1313 %c0 = arith.constant 0 : index1314 // Bufferizes in-place because `print_buffer` is read-only.1315 // CHECK: tensor.cast {{.*}} {__inplace_operands_attr__ = ["true"]}1316 %0 = tensor.cast %t : tensor<5xf32> to tensor<*xf32>1317 // CHECK: call @print_buffer(%cast) {__inplace_operands_attr__ = ["true"]}1318 func.call @print_buffer(%0) : (tensor<*xf32>) -> ()1319 %1 = tensor.extract %t[%c0] : tensor<5xf32>1320 return %1 : f321321}1322 1323// -----1324 1325// CHECK-LABEL: func.func private @ext_func(tensor<?xf32> {bufferization.access = "read-write"}, tensor<?xf32> {bufferization.access = "read-write"})1326func.func private @ext_func(%t1: tensor<?xf32>, %t2: tensor<?xf32>)1327 1328// CHECK: func.func @private_func_two_params_writing(%{{.*}}: tensor<?xf32> {bufferization.access = "read"})1329func.func @private_func_two_params_writing(%t: tensor<?xf32>) {1330 // Both operands bufferize out-of-place because both bufferize to a memory1331 // write.1332 // CHECK: call @ext_func(%{{.*}}, %{{.*}}) {__inplace_operands_attr__ = ["false", "false"]}1333 func.call @ext_func(%t, %t) : (tensor<?xf32>, tensor<?xf32>) -> ()1334 return1335}1336 1337// -----1338 1339// CHECK-LABEL: func.func private @ext_func(tensor<?xf32> {bufferization.access = "read-write"}) -> (tensor<5xf32>, tensor<6xf32>)1340func.func private @ext_func(%t: tensor<?xf32>) -> (tensor<5xf32>, tensor<6xf32>)1341 1342// CHECK: func.func @private_func_aliasing(%{{.*}}: tensor<?xf32> {bufferization.access = "read"})1343func.func @private_func_aliasing(%t: tensor<?xf32>) -> f32 {1344 %c0 = arith.constant 0 : index1345 // Bufferizes out-of-place because either one of the two reuslts may alias1346 // with the argument and one of the results is read afterwards.1347 // CHECK: call @ext_func(%{{.*}}) {__inplace_operands_attr__ = ["false"]} : (tensor<?xf32>) -> (tensor<5xf32>, tensor<6xf32>)1348 %0, %1 = func.call @ext_func(%t) : (tensor<?xf32>) -> (tensor<5xf32>, tensor<6xf32>)1349 %2 = tensor.extract %1[%c0] : tensor<6xf32>1350 return %2 : f321351}1352 1353// -----1354 1355// CHECK-LABEL: func @recursive_function1356func.func @recursive_function(%a: tensor<?xf32>, %b: tensor<?xf32>) -> (tensor<?xf32>, tensor<?xf32>) {1357 // The analysis does not support recursive function calls and is conservative1358 // around them.1359 // CHECK: call @recursive_function1360 // CHECK-SAME: {__inplace_operands_attr__ = ["false", "false"]}1361 %0:2 = call @recursive_function(%a, %b) : (tensor<?xf32>, tensor<?xf32>) -> (tensor<?xf32>, tensor<?xf32>)1362 return %0#0, %0#1 : tensor<?xf32>, tensor<?xf32>1363}1364 1365// -----1366 1367// CHECK-ALIAS-SETS-LABEL: func @multiple_returns(1368func.func @multiple_returns(%c: i1, %t0: tensor<5xf32>, %t1: tensor<5xf32>, %t2: tensor<5xf32>) -> tensor<5xf32> {1369 cf.cond_br %c, ^bb1, ^bb21370^bb1:1371 return %t0 : tensor<5xf32>1372^bb2:1373 return %t1 : tensor<5xf32>1374}1375 1376// CHECK-ALIAS-SETS: func @caller(1377// CHECK-ALIAS-SETS-SAME: %{{.*}}: i1, %[[t0:.*]]: tensor<5xf32> {bufferization.access = "read"}, %[[t1:.*]]: tensor<5xf32> {bufferization.access = "read"}, %[[t2:.*]]: tensor<5xf32> {bufferization.access = "none"})1378func.func @caller(%c: i1, %t0: tensor<5xf32>, %t1: tensor<5xf32>, %t2: tensor<5xf32>) {1379 // Check that alias sets are computed correctly.1380 // CHECK-ALIAS-SETS: %[[result:.*]] = call @multiple_returns1381 // CHECK-ALIAS-SETS-SAME: {__inplace_operands_attr__ = ["none", "true", "true", "true"],1382 // CHECK-ALIAS-SETS-SAME: __opresult_alias_set_attr__ = [{{\[}}"%[[result]]", "%[[t0]]", "%[[t1]]"]]}1383 call @multiple_returns(%c, %t0, %t1, %t2) : (i1, tensor<5xf32>, tensor<5xf32>, tensor<5xf32>) -> (tensor<5xf32>)1384 return1385}1386 1387// -----1388 1389// CHECK-ALIAS-SETS-LABEL: func @multiple_equivalent_returns(1390func.func @multiple_equivalent_returns(%c: i1, %t0: tensor<5xf32>, %t1: tensor<5xf32>, %t2: tensor<5xf32>) -> tensor<5xf32> {1391 cf.cond_br %c, ^bb1, ^bb21392^bb1:1393 return %t0 : tensor<5xf32>1394^bb2:1395 return %t0 : tensor<5xf32>1396}1397 1398// CHECK-ALIAS-SETS: func @caller(1399// CHECK-ALIAS-SETS-SAME: %{{.*}}: i1, %[[t0:.*]]: tensor<5xf32> {bufferization.access = "read"}, %[[t1:.*]]: tensor<5xf32> {bufferization.access = "none"}, %[[t2:.*]]: tensor<5xf32> {bufferization.access = "none"})1400func.func @caller(%c: i1, %t0: tensor<5xf32>, %t1: tensor<5xf32>, %t2: tensor<5xf32>) -> tensor<5xf32> {1401 // Check that equivalence sets are computed correctly.1402 // CHECK-ALIAS-SETS: %[[result:.*]] = call @multiple_equivalent_returns1403 // CHECK-ALIAS-SETS-SAME: {__inplace_operands_attr__ = ["none", "true", "true", "true"],1404 // CHECK-ALIAS-SETS-SAME: __opresult_alias_set_attr__ = [{{\[}}"%[[result]]", "%[[t0]]"]]}1405 %r = call @multiple_equivalent_returns(%c, %t0, %t1, %t2) : (i1, tensor<5xf32>, tensor<5xf32>, tensor<5xf32>) -> (tensor<5xf32>)1406 // CHECK-ALIAS-SETS-SAME: {__equivalent_func_args__ = [1], __inplace_operands_attr__ = ["true"]} %[[result]] : tensor<5xf32>1407 return %r : tensor<5xf32>1408}1409 1410// -----1411 1412// CHECK-ALIAS-LABEL: func @foo1413func.func @foo(%arg0: tensor<?xf32>) -> tensor<?xf32> {1414 // CHECK-ALIAS: return1415 // CHECK-ALIAS-SAME: __equivalent_func_args__ = [0]1416 return %arg0 : tensor<?xf32>1417}1418 1419// CHECK-ALIAS: func @bar(%[[arg0:.*]]: tensor<?xf32>1420func.func @bar(%arg0: tensor<?xf32>) -> tensor<?xf32> {1421 // CHECK-ALIAS: %[[call:.*]] = call @foo(%[[arg0]])1422 // CHECK-ALIAS-SAME: {__inplace_operands_attr__ = ["true"], __opresult_alias_set_attr__ = [{{\[}}"%[[call]]", "%[[arg0]]"]]}1423 %x = call @foo(%arg0) : (tensor<?xf32>) -> tensor<?xf32>1424 // CHECK-ALIAS: return1425 // CHECK-ALIAS-SAME: __equivalent_func_args__ = [0]1426 return %x : tensor<?xf32>1427}1428