101334d1aSMartin Erhart// RUN: mlir-opt -verify-diagnostics -ownership-based-buffer-deallocation \ 201334d1aSMartin Erhart// RUN: --buffer-deallocation-simplification -split-input-file %s | FileCheck %s 36a651c7fSMartin Erhart// RUN: mlir-opt -verify-diagnostics -ownership-based-buffer-deallocation=private-function-dynamic-ownership=true -split-input-file %s > /dev/null 401334d1aSMartin Erhart 508b7a71bSMartin Erhart// RUN: mlir-opt %s -buffer-deallocation-pipeline --split-input-file > /dev/null 608b7a71bSMartin Erhart 701334d1aSMartin Erhart// Test Case: Dead operations in a single block. 801334d1aSMartin Erhart// BufferDeallocation expected behavior: It only inserts the two missing 901334d1aSMartin Erhart// DeallocOps after the last BufferBasedOp. 1001334d1aSMartin Erhart 1101334d1aSMartin Erhart// CHECK-LABEL: func @redundantOperations 1201334d1aSMartin Erhartfunc.func @redundantOperations(%arg0: memref<2xf32>) { 1301334d1aSMartin Erhart %0 = memref.alloc() : memref<2xf32> 1401334d1aSMartin Erhart test.buffer_based in(%arg0: memref<2xf32>) out(%0: memref<2xf32>) 1501334d1aSMartin Erhart %1 = memref.alloc() : memref<2xf32> 1601334d1aSMartin Erhart test.buffer_based in(%0: memref<2xf32>) out(%1: memref<2xf32>) 1701334d1aSMartin Erhart return 1801334d1aSMartin Erhart} 1901334d1aSMartin Erhart 2001334d1aSMartin Erhart// CHECK: (%[[ARG0:.*]]: {{.*}}) 2101334d1aSMartin Erhart// CHECK: %[[FIRST_ALLOC:.*]] = memref.alloc() 2201334d1aSMartin Erhart// CHECK-NOT: bufferization.dealloc 2301334d1aSMartin Erhart// CHECK: test.buffer_based in(%[[ARG0]]{{.*}}out(%[[FIRST_ALLOC]] 2401334d1aSMartin Erhart// CHECK-NOT: bufferization.dealloc 2501334d1aSMartin Erhart// CHECK: %[[SECOND_ALLOC:.*]] = memref.alloc() 2601334d1aSMartin Erhart// CHECK-NOT: bufferization.dealloc 2701334d1aSMartin Erhart// CHECK: test.buffer_based in(%[[FIRST_ALLOC]]{{.*}}out(%[[SECOND_ALLOC]] 2801334d1aSMartin Erhart// CHECK: bufferization.dealloc (%[[FIRST_ALLOC]] :{{.*}}) if (%true{{[0-9_]*}}) 2901334d1aSMartin Erhart// CHECK: bufferization.dealloc (%[[SECOND_ALLOC]] :{{.*}}) if (%true{{[0-9_]*}}) 3001334d1aSMartin Erhart// CHECK-NEXT: return 3101334d1aSMartin Erhart 3201334d1aSMartin Erhart// TODO: The dealloc could be split in two to avoid runtime aliasing checks 3301334d1aSMartin Erhart// since we can be sure at compile time that they will never alias. 3401334d1aSMartin Erhart 3501334d1aSMartin Erhart// ----- 3601334d1aSMartin Erhart 3701334d1aSMartin Erhart// CHECK-LABEL: func @allocaIsNotDeallocated 3801334d1aSMartin Erhartfunc.func @allocaIsNotDeallocated(%arg0: memref<2xf32>) { 3901334d1aSMartin Erhart %0 = memref.alloc() : memref<2xf32> 4001334d1aSMartin Erhart test.buffer_based in(%arg0: memref<2xf32>) out(%0: memref<2xf32>) 4101334d1aSMartin Erhart %1 = memref.alloca() : memref<2xf32> 4201334d1aSMartin Erhart test.buffer_based in(%0: memref<2xf32>) out(%1: memref<2xf32>) 4301334d1aSMartin Erhart return 4401334d1aSMartin Erhart} 4501334d1aSMartin Erhart 4601334d1aSMartin Erhart// CHECK: (%[[ARG0:.*]]: {{.*}}) 4701334d1aSMartin Erhart// CHECK: %[[FIRST_ALLOC:.*]] = memref.alloc() 4801334d1aSMartin Erhart// CHECK-NEXT: test.buffer_based in(%[[ARG0]]{{.*}}out(%[[FIRST_ALLOC]] 4901334d1aSMartin Erhart// CHECK-NEXT: %[[SECOND_ALLOC:.*]] = memref.alloca() 5001334d1aSMartin Erhart// CHECK-NEXT: test.buffer_based in(%[[FIRST_ALLOC]]{{.*}}out(%[[SECOND_ALLOC]] 5101334d1aSMartin Erhart// CHECK: bufferization.dealloc (%[[FIRST_ALLOC]] :{{.*}}) if (%true{{[0-9_]*}}) 5201334d1aSMartin Erhart// CHECK-NEXT: return 5301334d1aSMartin Erhart 5401334d1aSMartin Erhart// ----- 5501334d1aSMartin Erhart 5601334d1aSMartin Erhart// Test Case: Inserting missing DeallocOp in a single block. 5701334d1aSMartin Erhart 5801334d1aSMartin Erhart// CHECK-LABEL: func @inserting_missing_dealloc_simple 5901334d1aSMartin Erhartfunc.func @inserting_missing_dealloc_simple( 6001334d1aSMartin Erhart %arg0 : memref<2xf32>, 6101334d1aSMartin Erhart %arg1: memref<2xf32>) { 6201334d1aSMartin Erhart %0 = memref.alloc() : memref<2xf32> 6301334d1aSMartin Erhart test.buffer_based in(%arg0: memref<2xf32>) out(%0: memref<2xf32>) 6401334d1aSMartin Erhart test.copy(%0, %arg1) : (memref<2xf32>, memref<2xf32>) 6501334d1aSMartin Erhart return 6601334d1aSMartin Erhart} 6701334d1aSMartin Erhart 6801334d1aSMartin Erhart// CHECK: %[[ALLOC0:.*]] = memref.alloc() 6901334d1aSMartin Erhart// CHECK: test.copy 7001334d1aSMartin Erhart// CHECK: bufferization.dealloc (%[[ALLOC0]] :{{.*}}) if (%true{{[0-9_]*}}) 7101334d1aSMartin Erhart 7201334d1aSMartin Erhart// ----- 7301334d1aSMartin Erhart 7401334d1aSMartin Erhart// Test Case: The ownership indicator is set to false for alloca 7501334d1aSMartin Erhart 7601334d1aSMartin Erhart// CHECK-LABEL: func @alloca_ownership_indicator_is_false 7701334d1aSMartin Erhartfunc.func @alloca_ownership_indicator_is_false() { 7801334d1aSMartin Erhart %0 = memref.alloca() : memref<2xf32> 7901334d1aSMartin Erhart cf.br ^bb1(%0: memref<2xf32>) 8001334d1aSMartin Erhart^bb1(%arg0 : memref<2xf32>): 8101334d1aSMartin Erhart return 8201334d1aSMartin Erhart} 8301334d1aSMartin Erhart 8401334d1aSMartin Erhart// CHECK: %[[ALLOC0:.*]] = memref.alloca() 8501334d1aSMartin Erhart// CHECK-NEXT: cf.br ^bb1(%[[ALLOC0]], %false : 8601334d1aSMartin Erhart// CHECK-NEXT: ^bb1([[A0:%.+]]: memref<2xf32>, [[COND0:%.+]]: i1): 8701334d1aSMartin Erhart// CHECK: [[BASE:%[a-zA-Z0-9_]+]]{{.*}} = memref.extract_strided_metadata [[A0]] 8801334d1aSMartin Erhart// CHECK: bufferization.dealloc ([[BASE]] : {{.*}}) if ([[COND0]]) 8901334d1aSMartin Erhart// CHECK-NEXT: return 9001334d1aSMartin Erhart 9101334d1aSMartin Erhart// ----- 9201334d1aSMartin Erhart 9301334d1aSMartin Erhartfunc.func @dealloc_existing_clones(%arg0: memref<?x?xf64>, %arg1: memref<?x?xf64>) -> memref<?x?xf64> { 9401334d1aSMartin Erhart %0 = bufferization.clone %arg0 : memref<?x?xf64> to memref<?x?xf64> 9501334d1aSMartin Erhart %1 = bufferization.clone %arg1 : memref<?x?xf64> to memref<?x?xf64> 9601334d1aSMartin Erhart return %0 : memref<?x?xf64> 9701334d1aSMartin Erhart} 9801334d1aSMartin Erhart 9901334d1aSMartin Erhart// CHECK-LABEL: func @dealloc_existing_clones 10001334d1aSMartin Erhart// CHECK: (%[[ARG0:.*]]: memref<?x?xf64>, %[[ARG1:.*]]: memref<?x?xf64>) 10101334d1aSMartin Erhart// CHECK: %[[RES0:.*]] = bufferization.clone %[[ARG0]] 10201334d1aSMartin Erhart// CHECK: %[[RES1:.*]] = bufferization.clone %[[ARG1]] 1036a651c7fSMartin Erhart// CHECK-NEXT: bufferization.dealloc (%[[RES1]] :{{.*}}) if (%true{{[0-9_]*}}) 10401334d1aSMartin Erhart// CHECK-NOT: retain 10501334d1aSMartin Erhart// CHECK-NEXT: return %[[RES0]] 10601334d1aSMartin Erhart 10701334d1aSMartin Erhart// TODO: The retain operand could be dropped to avoid runtime aliasing checks 10801334d1aSMartin Erhart// since We can guarantee at compile-time that it will never alias with the 10901334d1aSMartin Erhart// dealloc operand 1106a651c7fSMartin Erhart 1116a651c7fSMartin Erhart// ----- 1126a651c7fSMartin Erhart 1136a651c7fSMartin Erhartmemref.global "private" constant @__constant_4xf32 : memref<4xf32> = dense<[1.000000e+00, 2.000000e+00, 3.000000e+00, 4.000000e+00]> 1146a651c7fSMartin Erhart 1156a651c7fSMartin Erhartfunc.func @op_without_aliasing_and_allocation() -> memref<4xf32> { 1166a651c7fSMartin Erhart %0 = memref.get_global @__constant_4xf32 : memref<4xf32> 1176a651c7fSMartin Erhart return %0 : memref<4xf32> 1186a651c7fSMartin Erhart} 1196a651c7fSMartin Erhart 1206a651c7fSMartin Erhart// CHECK-LABEL: func @op_without_aliasing_and_allocation 1216a651c7fSMartin Erhart// CHECK: [[GLOBAL:%.+]] = memref.get_global @__constant_4xf32 1226a651c7fSMartin Erhart// CHECK: [[RES:%.+]] = scf.if %false 1236a651c7fSMartin Erhart// CHECK: scf.yield [[GLOBAL]] : 1246a651c7fSMartin Erhart// CHECK: [[CLONE:%.+]] = bufferization.clone [[GLOBAL]] 1256a651c7fSMartin Erhart// CHECK: scf.yield [[CLONE]] : 1266a651c7fSMartin Erhart// CHECK: return [[RES]] : 127*4d80eff8SMatthias Springer 128*4d80eff8SMatthias Springer// ----- 129*4d80eff8SMatthias Springer 130*4d80eff8SMatthias Springer// Allocations with "bufferization.manual_deallocation" are assigned an 131*4d80eff8SMatthias Springer// ownership of "false". 132*4d80eff8SMatthias Springer 133*4d80eff8SMatthias Springerfunc.func @manual_deallocation(%c: i1, %f: f32, %idx: index) -> f32 { 134*4d80eff8SMatthias Springer %0 = memref.alloc() {bufferization.manual_deallocation} : memref<5xf32> 135*4d80eff8SMatthias Springer linalg.fill ins(%f : f32) outs(%0 : memref<5xf32>) 136*4d80eff8SMatthias Springer %1 = memref.alloc() : memref<5xf32> 137*4d80eff8SMatthias Springer linalg.fill ins(%f : f32) outs(%1 : memref<5xf32>) 138*4d80eff8SMatthias Springer %2 = arith.select %c, %0, %1 : memref<5xf32> 139*4d80eff8SMatthias Springer %3 = memref.load %2[%idx] : memref<5xf32> 140*4d80eff8SMatthias Springer 141*4d80eff8SMatthias Springer // Only buffers that are under "manual deallocation" are allowed to be 142*4d80eff8SMatthias Springer // deallocated with memref.dealloc. For consistency reasons, the 143*4d80eff8SMatthias Springer // manual_deallocation attribute must also be specified. A runtime insertion 144*4d80eff8SMatthias Springer // is inserted to ensure that we do not have ownership. (This is not a 145*4d80eff8SMatthias Springer // bulletproof check, but covers some cases of invalid IR.) 146*4d80eff8SMatthias Springer memref.dealloc %0 {bufferization.manual_deallocation} : memref<5xf32> 147*4d80eff8SMatthias Springer 148*4d80eff8SMatthias Springer return %3 : f32 149*4d80eff8SMatthias Springer} 150*4d80eff8SMatthias Springer 151*4d80eff8SMatthias Springer// CHECK-LABEL: func @manual_deallocation( 152*4d80eff8SMatthias Springer// CHECK: %[[true:.*]] = arith.constant true 153*4d80eff8SMatthias Springer// CHECK: %[[manual_alloc:.*]] = memref.alloc() {bufferization.manual_deallocation} : memref<5xf32> 154*4d80eff8SMatthias Springer// CHECK: %[[managed_alloc:.*]] = memref.alloc() : memref<5xf32> 155*4d80eff8SMatthias Springer// CHECK: %[[selected:.*]] = arith.select 156*4d80eff8SMatthias Springer// CHECK: cf.assert %[[true]], "expected that the block does not have ownership" 157*4d80eff8SMatthias Springer// CHECK: memref.dealloc %[[manual_alloc]] 158*4d80eff8SMatthias Springer// CHECK: bufferization.dealloc (%[[managed_alloc]] : memref<5xf32>) if (%[[true]]) 159