1// RUN: mlir-opt %s -one-shot-bufferize="allow-return-allocs bufferize-function-boundaries" -drop-equivalent-buffer-results -split-input-file | FileCheck %s 2 3// Run fuzzer with different seeds. 4// RUN: mlir-opt %s -one-shot-bufferize="allow-return-allocs test-analysis-only analysis-fuzzer-seed=23 bufferize-function-boundaries" -split-input-file -o /dev/null 5// RUN: mlir-opt %s -one-shot-bufferize="allow-return-allocs test-analysis-only analysis-fuzzer-seed=59 bufferize-function-boundaries" -split-input-file -o /dev/null 6// RUN: mlir-opt %s -one-shot-bufferize="allow-return-allocs test-analysis-only analysis-fuzzer-seed=91 bufferize-function-boundaries" -split-input-file -o /dev/null 7 8// Test bufferization using memref types that have no layout map. 9// RUN: mlir-opt %s -one-shot-bufferize="allow-return-allocs unknown-type-conversion=identity-layout-map bufferize-function-boundaries" -split-input-file -o /dev/null 10 11// CHECK-DAG: #[[$map_1d_dyn:.*]] = affine_map<(d0)[s0, s1] -> (d0 * s1 + s0)> 12 13// CHECK-LABEL: func @insert_slice_fun 14// CHECK-SAME: %[[A0:[a-zA-Z0-9]*]]: memref<?xf32, #[[$map_1d_dyn]]>, 15// CHECK-SAME: %[[A1:[a-zA-Z0-9]*]]: memref<?xf32, #[[$map_1d_dyn]]>, 16// CHECK-SAME: %[[t0:[a-zA-Z0-9]*]]: memref<4xf32, #[[$map_1d_dyn]]>, 17// CHECK-SAME: %[[t1:[a-zA-Z0-9]*]]: memref<4xf32, #[[$map_1d_dyn]]> 18func.func @insert_slice_fun( 19 %A0 : tensor<?xf32> {bufferization.writable = false}, 20 %A1 : tensor<?xf32> {bufferization.writable = true}, 21 %t0 : tensor<4xf32> {bufferization.writable = false}, 22 %t1 : tensor<4xf32> {bufferization.writable = true}) 23 -> (tensor<?xf32>, tensor<?xf32>, tensor<?xf32>, tensor<?xf32>) 24{ 25 // Hoisted allocs. 26 // CHECK: %[[REALLOC1:.*]] = memref.alloc 27 // CHECK: %[[REALLOC2:.*]] = memref.alloc 28 // CHECK: %[[REALLOC3:.*]] = memref.alloc 29 30 // Alloc and copy the whole result tensor. Copy the tensor.extract_slice. 31 // CHECK: memref.copy %[[A0]], %[[REALLOC3]] 32 // CHECK: %[[SV_A0:.*]] = memref.subview %[[REALLOC3]] 33 // CHECK: memref.copy %[[t0]], %[[SV_A0]] 34 %r0 = tensor.insert_slice %t0 into %A0[0][4][1] : tensor<4xf32> into tensor<?xf32> 35 36 // Alloc and copy the whole result tensor. Copy the tensor.extract_slice. 37 // CHECK: memref.copy %[[A0]] 38 // CHECK: %[[SV_A0_2:.*]] = memref.subview %[[REALLOC2]] 39 // CHECK: memref.copy %[[t1]], %[[SV_A0_2]] 40 %r1 = tensor.insert_slice %t1 into %A0[0][4][1] : tensor<4xf32> into tensor<?xf32> 41 42 // Still alloc the large tensor because %A1 is read after. Copy the tensor.extract_slice. 43 // CHECK: memref.copy %[[A1]] 44 // CHECK: %[[SV_A1:.*]] = memref.subview %[[REALLOC1]] 45 // CHECK: memref.copy %[[t0]], %[[SV_A1]] 46 %r2 = tensor.insert_slice %t0 into %A1[0][4][1] : tensor<4xf32> into tensor<?xf32> 47 48 // Do not realloc the large tensor. Copy the tensor.extract_slice. 49 // CHECK-NOT: alloc 50 // CHECK: %[[SV_A1_2:.*]] = memref.subview %[[A1]] 51 // CHECK: memref.copy %[[t1]], %[[SV_A1_2]] 52 %r3 = tensor.insert_slice %t1 into %A1[0][4][1] : tensor<4xf32> into tensor<?xf32> 53 54 // CHECK: return %[[REALLOC3]], %[[REALLOC2]], %[[REALLOC1]] : 55 // CHECK-SAME: memref<?xf32>, memref<?xf32>, memref<?xf32> 56 return %r0, %r1, %r2, %r3: tensor<?xf32>, tensor<?xf32>, tensor<?xf32>, tensor<?xf32> 57} 58 59// ----- 60 61// CHECK-DAG: #[[$map_1d_dyn:.*]] = affine_map<(d0)[s0, s1] -> (d0 * s1 + s0)> 62 63// CHECK-LABEL: func @insert_slice_fun 64// CHECK-SAME: %[[A:[a-zA-Z0-9]*]]: memref<?xf32, #[[$map_1d_dyn]]> 65// CHECK-SAME: %[[t:[a-zA-Z0-9]*]]: memref<4xf32, #[[$map_1d_dyn]]> 66func.func @insert_slice_fun( 67 %A : tensor<?xf32> {bufferization.writable = true}, 68 %t : tensor<4xf32> {bufferization.writable = false}) 69 -> tensor<?xf32> 70{ 71 %f0 = arith.constant 0.0 : f32 72 73 // CHECK-NOT: alloc 74 // CHECK: %[[SV_A:.*]] = memref.subview %[[A]] 75 // CHECK: memref.copy %[[t]], %[[SV_A]] 76 %r0 = tensor.insert_slice %t into %A[0][4][1] : tensor<4xf32> into tensor<?xf32> 77 78 /// Overwrite A inplace. 79 // CHECK: linalg.fill ins({{.*}}{{.*}}outs(%[[A]] 80 %r1 = linalg.fill ins(%f0 : f32) outs(%r0 : tensor<?xf32>) -> tensor<?xf32> 81 82 // CHECK: return 83 // CHECK-NOT: tensor 84 return %r1: tensor<?xf32> 85} 86 87// ----- 88 89// CHECK-DAG: #[[$map_1d_dyn:.*]] = affine_map<(d0)[s0, s1] -> (d0 * s1 + s0)> 90 91// CHECK-LABEL: func @insert_slice_fun 92// CHECK-SAME: %[[A:[a-zA-Z0-9]*]]: memref<?xf32, #[[$map_1d_dyn]]> 93// CHECK-SAME: %[[t:[a-zA-Z0-9]*]]: memref<4xf32, #[[$map_1d_dyn]]> 94func.func @insert_slice_fun( 95 %A : tensor<?xf32> {bufferization.writable = true}, 96 %t : tensor<4xf32> {bufferization.writable = false}) 97 -> tensor<?xf32> 98{ 99 %f0 = arith.constant 0.0 : f32 100 101 // CHECK: linalg.fill ins({{.*}}{{.*}}outs(%[[A]] 102 %r0 = linalg.fill ins(%f0 : f32) outs(%A : tensor<?xf32>) -> tensor<?xf32> 103 104 // CHECK-NOT: alloc 105 // CHECK: %[[SV_A:.*]] = memref.subview %[[A]] 106 /// Overwrite A inplace by copying into the subview. 107 // CHECK: memref.copy %[[t]], %[[SV_A]] 108 %r1 = tensor.insert_slice %t into %r0[0][4][1] : tensor<4xf32> into tensor<?xf32> 109 110 // CHECK: return 111 // CHECK-NOT: tensor 112 return %r1: tensor<?xf32> 113} 114 115// ----- 116 117// CHECK-DAG: #[[$map_1d_dyn:.*]] = affine_map<(d0)[s0, s1] -> (d0 * s1 + s0)> 118 119// CHECK-LABEL: func @insert_slice_fun_not_inplace 120// CHECK-SAME: %[[A:[a-zA-Z0-9]*]]: memref<?xf32, #[[$map_1d_dyn]]> 121// CHECK-SAME: %[[t:[a-zA-Z0-9]*]]: memref<4xf32, #[[$map_1d_dyn]]> 122func.func @insert_slice_fun_not_inplace( 123 %A : tensor<?xf32> {bufferization.writable = false}, 124 %t : tensor<4xf32> {bufferization.writable = false}) 125 -> tensor<?xf32> 126{ 127 // CHECK: %[[ALLOC:.*]] = memref.alloc(%{{.*}}) {alignment = 128 : i64} : memref<?xf32> 128 // CHECK: memref.copy %[[A]], %[[ALLOC]] : memref<?xf32{{.*}} to memref<?xf32> 129 // CHECK: %[[SV:.*]] = memref.subview %[[ALLOC]][0] [4] [1] : memref<?xf32> to memref<4xf32> 130 // CHECK: memref.copy %[[t]], %[[SV]] : memref<4xf32, #map> to memref<4xf32> 131 %r0 = tensor.insert_slice %t into %A[0][4][1] : tensor<4xf32> into tensor<?xf32> 132 133 // CHECK: return %{{.*}} : memref<?xf32> 134 return %r0: tensor<?xf32> 135} 136 137// ----- 138 139// CHECK-LABEL: func @tensor_cast_not_in_place( 140// CHECK-SAME: %[[A:.*]]: memref<?xf32{{.*}}>, %[[B:.*]]: memref<?xf32{{.*}}> 141// CHECK: %[[alloc:.*]] = memref.alloc 142// CHECK: memref.copy %[[A]], %[[alloc]] 143// CHECK: %[[subview:.*]] = memref.subview %[[A]][{{.*}}] [4] [1] : {{.*}} to memref<4xf32 144// CHECK: memref.copy %[[alloc]], %[[subview]] 145func.func @tensor_cast_not_in_place( 146 %A : tensor<?xf32> {bufferization.writable = true}, 147 %B : tensor<?xf32> {bufferization.writable = false}, %idx: index) 148 -> (tensor<?xf32>) 149{ 150 %r0 = tensor.cast %A : tensor<?xf32> to tensor<4xf32> 151 %r1 = tensor.insert_slice %r0 into %A[%idx][4][1] : tensor<4xf32> into tensor<?xf32> 152 return %r1 : tensor<?xf32> 153} 154 155// ----- 156 157// CHECK-LABEL: func @insert_op 158// CHECK-SAME: %[[t1:.*]]: memref<?xf32, {{.*}}>, %[[s:.*]]: f32, %[[i:.*]]: index 159func.func @insert_op(%t1 : tensor<?xf32> {bufferization.writable = true}, 160 %s : f32, %i : index) -> tensor<?xf32> { 161 // CHECK: memref.store %[[s]], %[[t1]][%[[i]]] 162 %0 = tensor.insert %s into %t1[%i] : tensor<?xf32> 163 // CHECK: return 164 return %0 : tensor<?xf32> 165} 166 167// ----- 168 169// A regression test to make sure that we handle rank-reducing extract_slice 170// correctly. 171 172// CHECK-LABEL: func @rank_reducing 173func.func @rank_reducing( 174 %i: index, %j: index, 175 %arg0: tensor<8x18x32xf32>) 176 -> tensor<?x1x6x8xf32> { 177 %c1 = arith.constant 1 : index 178 %c6 = arith.constant 6 : index 179 %c8 = arith.constant 8 : index 180 %c32 = arith.constant 32 : index 181 %c0 = arith.constant 0 : index 182 %0 = bufferization.alloc_tensor() : tensor<4x1x6x8xf32> 183 %1 = tensor.cast %0 : tensor<4x1x6x8xf32> to tensor<?x1x6x8xf32> 184 %2 = bufferization.alloc_tensor() : tensor<1x6x8xf32> 185 %5 = scf.for %arg7 = %c0 to %c32 step %c8 iter_args(%arg8 = %1) -> (tensor<?x1x6x8xf32>) { 186 %7 = affine.apply affine_map<(d0) -> (d0 ceildiv 8)>(%arg7) 187 %8 = tensor.extract_slice %arg0[%i, %j, %arg7] [1, 6, 8] [1, 1, 1] : tensor<8x18x32xf32> to tensor<1x6x8xf32> 188 %9 = scf.for %arg9 = %c0 to %c6 step %c1 iter_args(%arg10 = %2) -> (tensor<1x6x8xf32>) { 189 %11 = tensor.extract_slice %8[0, %arg9, 0] [1, 1, 8] [1, 1, 1] : tensor<1x6x8xf32> to tensor<1x1x8xf32> 190 %12 = tensor.insert_slice %11 into %arg10[0, %arg9, 0] [1, 1, 8] [1, 1, 1] : tensor<1x1x8xf32> into tensor<1x6x8xf32> 191 scf.yield %12 : tensor<1x6x8xf32> 192 } 193 %10 = tensor.insert_slice %9 into %arg8[%7, 0, 0, 0] [1, 1, 6, 8] [1, 1, 1, 1] : tensor<1x6x8xf32> into tensor<?x1x6x8xf32> 194 scf.yield %10 : tensor<?x1x6x8xf32> 195 } 196 return %5: tensor<?x1x6x8xf32> 197} 198