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