1// RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries=1 " -drop-equivalent-buffer-results -split-input-file | FileCheck %s
2// RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries=1 " -split-input-file | FileCheck %s --check-prefix=NO-DROP
3
4// Run fuzzer with different seeds.
5// RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries=1 test-analysis-only analysis-heuristic=fuzzer analysis-fuzzer-seed=23" -split-input-file -o /dev/null
6// RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries=1 test-analysis-only analysis-heuristic=fuzzer analysis-fuzzer-seed=59" -split-input-file -o /dev/null
7// RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries=1 test-analysis-only analysis-heuristic=fuzzer analysis-fuzzer-seed=91" -split-input-file -o /dev/null
8
9// Test bufferization using memref types that have no layout map.
10// RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries=1 unknown-type-conversion=identity-layout-map function-boundary-type-conversion=identity-layout-map" -split-input-file -o /dev/null
11
12// Make sure that the returned buffer is not deallocated.
13// TODO: Such buffers currently leak. We need buffer hoisting / ref counting for
14// this in the future.
15
16// CHECK-LABEL: func @create_tensor() -> memref<10xf32> {
17//       CHECK:   %[[alloc:.*]] = memref.alloc
18//       CHECK:   return %[[alloc]]
19func.func @create_tensor() -> tensor<10xf32> {
20  %0 = bufferization.alloc_tensor() : tensor<10xf32>
21  return %0 : tensor<10xf32>
22}
23
24// CHECK: func @caller(
25// CHECK: %[[call:.*]] = call @create_tensor() : () -> memref<10xf32>
26// CHECK: %[[extracted:.*]] = memref.load %[[call]]
27// CHECK: return %[[extracted]]
28func.func @caller(%idx: index) -> f32 {
29  %0 = call @create_tensor() : () -> (tensor<10xf32>)
30  %1 = tensor.extract %0[%idx] : tensor<10xf32>
31  return %1 : f32
32}
33
34// -----
35
36// return_slice returns an aliasing tensor. In main, %t is overwritten (but not
37// read). This is a conflict because %0 is aliasing with %t. An alloc + copy is
38// needed.
39
40// CHECK-LABEL: func @return_slice(
41//   CHECK-NOT:   alloc
42//   CHECK-NOT:   copy
43//       CHECK:   memref.subview
44func.func @return_slice(%t: tensor<?xf32>, %sz: index) -> (tensor<?xf32>) {
45  %0 = tensor.extract_slice %t[4][%sz][1] : tensor<?xf32> to tensor<?xf32>
46  return %0 : tensor<?xf32>
47}
48
49// CHECK-LABEL: func @main(
50//  CHECK-SAME:     %[[t:.*]]: memref<?xf32
51//       CHECK:   %[[call:.*]] = call @return_slice(%[[t]]
52//       CHECK:   %[[alloc:.*]] = memref.alloc
53//       CHECK:   memref.copy %[[call]], %[[alloc]]
54//       CHECK:   linalg.fill ins({{.*}}) outs(%[[t]]
55//       CHECK:   memref.load %[[alloc]]
56//       CHECK:   memref.load %[[t]]
57func.func @main(%t: tensor<?xf32>, %sz: index, %idx: index) -> (f32, f32) {
58  %cst = arith.constant 1.0 : f32
59  %0 = call @return_slice(%t, %sz) : (tensor<?xf32>, index) -> (tensor<?xf32>)
60  %filled = linalg.fill ins(%cst : f32) outs(%t : tensor<?xf32>) -> tensor<?xf32>
61  %r1 = tensor.extract %0[%idx] : tensor<?xf32>
62  %r2 = tensor.extract %filled[%idx] : tensor<?xf32>
63  return %r1, %r2 : f32, f32
64}
65
66// -----
67
68func.func @return_arg(%A: tensor<?xf32>) -> tensor<?xf32> {
69  func.return %A : tensor<?xf32>
70}
71// CHECK-LABEL: func @return_arg
72// CHECK-SAME:      %[[A:.*]]: memref<?xf32
73//  CHECK-NOT:    return %[[A]]
74
75// NO-DROP-LABEL: func @return_arg
76//  NO-DROP-SAME:     %[[A:.*]]: memref<?xf32
77//       NO-DROP:   return %[[A]]
78