
This patch generalizes the MemoryAllocation pass (alloca -> heap) to handle fir.alloca regardless of their postion in the IR. Currently, it only dealt with fir.alloca in function entry blocks. The logic is placed in a utility that can be used to replace alloca in an operation on demand to whatever kind of allocation the utility user wants via callbacks (allocmem, or custom runtime calls to instrument the code...). To do so, a concept of ownership, that was already implied a bit and used in passes like stack-reclaim, is formalized. Any operation with the LoopLikeInterface, AutomaticAllocationScope, or IsolatedFromAbove owns the alloca directly nested inside its regions, and they must not be used after the operation. The pass then looks for the exit points of region with such interface, and use that to insert deallocation. If dominance is not proved, the pass fallbacks to storing the new address into a C pointer variable created in the entry of the owning region which allows inserting deallocation as needed, included near the alloca itself to avoid leaks when the alloca is executed multiple times due to block CFGs loops. This should fix https://github.com/llvm/llvm-project/issues/88344. In a next step, I will try to refactor lowering a bit to introduce lifetime operation for alloca so that the deallocation points can be inserted as soon as possible.
162 lines
8.0 KiB
Plaintext
162 lines
8.0 KiB
Plaintext
// Test memory allocation pass for fir.alloca outside of function entry block
|
|
// RUN: fir-opt --memory-allocation-opt="dynamic-array-on-heap=true" %s | FileCheck %s
|
|
|
|
func.func @test_loop() {
|
|
%c1 = arith.constant 1 : index
|
|
%c100 = arith.constant 100 : index
|
|
fir.do_loop %arg0 = %c1 to %c100 step %c1 {
|
|
%1 = fir.alloca !fir.array<?xf32>, %arg0
|
|
fir.call @bar(%1) : (!fir.ref<!fir.array<?xf32>>) -> ()
|
|
fir.result
|
|
}
|
|
return
|
|
}
|
|
// CHECK-LABEL: func.func @test_loop() {
|
|
// CHECK: %[[VAL_0:.*]] = arith.constant 1 : index
|
|
// CHECK: %[[VAL_1:.*]] = arith.constant 100 : index
|
|
// CHECK: fir.do_loop %[[VAL_2:.*]] = %[[VAL_0]] to %[[VAL_1]] step %[[VAL_0]] {
|
|
// CHECK: %[[VAL_3:.*]] = fir.allocmem !fir.array<?xf32>, %[[VAL_2]] {bindc_name = "", uniq_name = ""}
|
|
// CHECK: %[[VAL_4:.*]] = fir.convert %[[VAL_3]] : (!fir.heap<!fir.array<?xf32>>) -> !fir.ref<!fir.array<?xf32>>
|
|
// CHECK: fir.call @bar(%[[VAL_4]]) : (!fir.ref<!fir.array<?xf32>>) -> ()
|
|
// CHECK: fir.freemem %[[VAL_3]] : !fir.heap<!fir.array<?xf32>>
|
|
// CHECK: }
|
|
// CHECK: return
|
|
// CHECK: }
|
|
|
|
func.func @test_unstructured(%n : index) {
|
|
%c0 = arith.constant 0 : index
|
|
%c1 = arith.constant 1 : index
|
|
%c100 = arith.constant 100 : index
|
|
%0 = fir.alloca index
|
|
fir.store %c100 to %0 : !fir.ref<index>
|
|
cf.br ^bb1
|
|
^bb1: // 2 preds: ^bb0, ^bb4
|
|
%5 = fir.load %0 : !fir.ref<index>
|
|
%6 = arith.cmpi sgt, %5, %c0 : index
|
|
cf.cond_br %6, ^bb2, ^bb5
|
|
^bb2: // pred: ^bb1
|
|
%1 = fir.alloca !fir.array<?xf32>, %5
|
|
fir.call @bar(%1) : (!fir.ref<!fir.array<?xf32>>) -> ()
|
|
%25 = arith.cmpi slt, %5, %n : index
|
|
cf.cond_br %25, ^bb3, ^bb4
|
|
^bb3: // pred: ^bb2
|
|
fir.call @abort() : () -> ()
|
|
fir.unreachable
|
|
^bb4: // pred: ^bb2
|
|
%28 = arith.subi %5, %c1 : index
|
|
fir.store %28 to %0 : !fir.ref<index>
|
|
cf.br ^bb1
|
|
^bb5: // pred: ^bb1
|
|
return
|
|
}
|
|
// CHECK-LABEL: func.func @test_unstructured(
|
|
// CHECK-SAME: %[[VAL_0:.*]]: index) {
|
|
// CHECK: %[[VAL_1:.*]] = fir.alloca !fir.heap<!fir.array<?xf32>>
|
|
// CHECK: %[[VAL_2:.*]] = fir.zero_bits !fir.heap<!fir.array<?xf32>>
|
|
// CHECK: fir.store %[[VAL_2]] to %[[VAL_1]] : !fir.ref<!fir.heap<!fir.array<?xf32>>>
|
|
// CHECK: %[[VAL_3:.*]] = arith.constant 0 : i64
|
|
// CHECK: %[[VAL_4:.*]] = arith.constant 0 : index
|
|
// CHECK: %[[VAL_5:.*]] = arith.constant 1 : index
|
|
// CHECK: %[[VAL_6:.*]] = arith.constant 100 : index
|
|
// CHECK: %[[VAL_7:.*]] = fir.alloca index
|
|
// CHECK: fir.store %[[VAL_6]] to %[[VAL_7]] : !fir.ref<index>
|
|
// CHECK: cf.br ^bb1
|
|
// CHECK: ^bb1:
|
|
// CHECK: %[[VAL_8:.*]] = fir.load %[[VAL_7]] : !fir.ref<index>
|
|
// CHECK: %[[VAL_9:.*]] = arith.cmpi sgt, %[[VAL_8]], %[[VAL_4]] : index
|
|
// CHECK: cf.cond_br %[[VAL_9]], ^bb2, ^bb5
|
|
// CHECK: ^bb2:
|
|
// CHECK: %[[VAL_10:.*]] = fir.load %[[VAL_1]] : !fir.ref<!fir.heap<!fir.array<?xf32>>>
|
|
// CHECK: %[[VAL_11:.*]] = fir.convert %[[VAL_10]] : (!fir.heap<!fir.array<?xf32>>) -> i64
|
|
// CHECK: %[[VAL_12:.*]] = arith.cmpi ne, %[[VAL_11]], %[[VAL_3]] : i64
|
|
// CHECK: fir.if %[[VAL_12]] {
|
|
// CHECK: fir.freemem %[[VAL_10]] : !fir.heap<!fir.array<?xf32>>
|
|
// CHECK: }
|
|
// CHECK: %[[VAL_13:.*]] = fir.allocmem !fir.array<?xf32>, %[[VAL_8]] {bindc_name = "", uniq_name = ""}
|
|
// CHECK: %[[VAL_14:.*]] = fir.convert %[[VAL_13]] : (!fir.heap<!fir.array<?xf32>>) -> !fir.ref<!fir.array<?xf32>>
|
|
// CHECK: fir.store %[[VAL_13]] to %[[VAL_1]] : !fir.ref<!fir.heap<!fir.array<?xf32>>>
|
|
// CHECK: fir.call @bar(%[[VAL_14]]) : (!fir.ref<!fir.array<?xf32>>) -> ()
|
|
// CHECK: %[[VAL_15:.*]] = arith.cmpi slt, %[[VAL_8]], %[[VAL_0]] : index
|
|
// CHECK: cf.cond_br %[[VAL_15]], ^bb3, ^bb4
|
|
// CHECK: ^bb3:
|
|
// CHECK: fir.call @abort() : () -> ()
|
|
// CHECK: %[[VAL_16:.*]] = fir.load %[[VAL_1]] : !fir.ref<!fir.heap<!fir.array<?xf32>>>
|
|
// CHECK: %[[VAL_17:.*]] = fir.convert %[[VAL_16]] : (!fir.heap<!fir.array<?xf32>>) -> i64
|
|
// CHECK: %[[VAL_18:.*]] = arith.cmpi ne, %[[VAL_17]], %[[VAL_3]] : i64
|
|
// CHECK: fir.if %[[VAL_18]] {
|
|
// CHECK: fir.freemem %[[VAL_16]] : !fir.heap<!fir.array<?xf32>>
|
|
// CHECK: }
|
|
// CHECK: fir.unreachable
|
|
// CHECK: ^bb4:
|
|
// CHECK: %[[VAL_19:.*]] = arith.subi %[[VAL_8]], %[[VAL_5]] : index
|
|
// CHECK: fir.store %[[VAL_19]] to %[[VAL_7]] : !fir.ref<index>
|
|
// CHECK: cf.br ^bb1
|
|
// CHECK: ^bb5:
|
|
// CHECK: %[[VAL_20:.*]] = fir.load %[[VAL_1]] : !fir.ref<!fir.heap<!fir.array<?xf32>>>
|
|
// CHECK: %[[VAL_21:.*]] = fir.convert %[[VAL_20]] : (!fir.heap<!fir.array<?xf32>>) -> i64
|
|
// CHECK: %[[VAL_22:.*]] = arith.cmpi ne, %[[VAL_21]], %[[VAL_3]] : i64
|
|
// CHECK: fir.if %[[VAL_22]] {
|
|
// CHECK: fir.freemem %[[VAL_20]] : !fir.heap<!fir.array<?xf32>>
|
|
// CHECK: }
|
|
// CHECK: return
|
|
// CHECK: }
|
|
|
|
func.func @alloca_dominate_return_in_cycle(%arg0: index) {
|
|
%0 = fir.alloca index
|
|
%c1 = arith.constant 1 : index
|
|
fir.store %c1 to %0 : !fir.ref<index>
|
|
cf.br ^bb1
|
|
^bb1: // 2 preds: ^bb0, ^bb2
|
|
%1 = fir.load %0 : !fir.ref<index>
|
|
%2 = fir.alloca !fir.array<?xf32>, %1
|
|
fir.call @bar(%2) : (!fir.ref<!fir.array<?xf32>>) -> ()
|
|
%3 = arith.addi %1, %c1 : index
|
|
fir.store %3 to %0 : !fir.ref<index>
|
|
%4 = arith.cmpi slt, %3, %arg0 : index
|
|
cf.cond_br %4, ^bb2, ^bb3
|
|
^bb2: // pred: ^bb1
|
|
cf.br ^bb1
|
|
^bb3: // pred: ^bb1
|
|
return
|
|
}
|
|
// CHECK-LABEL: func.func @alloca_dominate_return_in_cycle(
|
|
// CHECK-SAME: %[[VAL_0:.*]]: index) {
|
|
// CHECK: %[[VAL_1:.*]] = fir.alloca !fir.heap<!fir.array<?xf32>>
|
|
// CHECK: %[[VAL_2:.*]] = fir.zero_bits !fir.heap<!fir.array<?xf32>>
|
|
// CHECK: fir.store %[[VAL_2]] to %[[VAL_1]] : !fir.ref<!fir.heap<!fir.array<?xf32>>>
|
|
// CHECK: %[[VAL_3:.*]] = arith.constant 0 : i64
|
|
// CHECK: %[[VAL_4:.*]] = fir.alloca index
|
|
// CHECK: %[[VAL_5:.*]] = arith.constant 1 : index
|
|
// CHECK: fir.store %[[VAL_5]] to %[[VAL_4]] : !fir.ref<index>
|
|
// CHECK: cf.br ^bb1
|
|
// CHECK: ^bb1:
|
|
// CHECK: %[[VAL_6:.*]] = fir.load %[[VAL_4]] : !fir.ref<index>
|
|
// CHECK: %[[VAL_7:.*]] = fir.load %[[VAL_1]] : !fir.ref<!fir.heap<!fir.array<?xf32>>>
|
|
// CHECK: %[[VAL_8:.*]] = fir.convert %[[VAL_7]] : (!fir.heap<!fir.array<?xf32>>) -> i64
|
|
// CHECK: %[[VAL_9:.*]] = arith.cmpi ne, %[[VAL_8]], %[[VAL_3]] : i64
|
|
// CHECK: fir.if %[[VAL_9]] {
|
|
// CHECK: fir.freemem %[[VAL_7]] : !fir.heap<!fir.array<?xf32>>
|
|
// CHECK: }
|
|
// CHECK: %[[VAL_10:.*]] = fir.allocmem !fir.array<?xf32>, %[[VAL_6]] {bindc_name = "", uniq_name = ""}
|
|
// CHECK: %[[VAL_11:.*]] = fir.convert %[[VAL_10]] : (!fir.heap<!fir.array<?xf32>>) -> !fir.ref<!fir.array<?xf32>>
|
|
// CHECK: fir.store %[[VAL_10]] to %[[VAL_1]] : !fir.ref<!fir.heap<!fir.array<?xf32>>>
|
|
// CHECK: fir.call @bar(%[[VAL_11]]) : (!fir.ref<!fir.array<?xf32>>) -> ()
|
|
// CHECK: %[[VAL_12:.*]] = arith.addi %[[VAL_6]], %[[VAL_5]] : index
|
|
// CHECK: fir.store %[[VAL_12]] to %[[VAL_4]] : !fir.ref<index>
|
|
// CHECK: %[[VAL_13:.*]] = arith.cmpi slt, %[[VAL_12]], %[[VAL_0]] : index
|
|
// CHECK: cf.cond_br %[[VAL_13]], ^bb2, ^bb3
|
|
// CHECK: ^bb2:
|
|
// CHECK: cf.br ^bb1
|
|
// CHECK: ^bb3:
|
|
// CHECK: %[[VAL_14:.*]] = fir.load %[[VAL_1]] : !fir.ref<!fir.heap<!fir.array<?xf32>>>
|
|
// CHECK: %[[VAL_15:.*]] = fir.convert %[[VAL_14]] : (!fir.heap<!fir.array<?xf32>>) -> i64
|
|
// CHECK: %[[VAL_16:.*]] = arith.cmpi ne, %[[VAL_15]], %[[VAL_3]] : i64
|
|
// CHECK: fir.if %[[VAL_16]] {
|
|
// CHECK: fir.freemem %[[VAL_14]] : !fir.heap<!fir.array<?xf32>>
|
|
// CHECK: }
|
|
// CHECK: return
|
|
// CHECK: }
|
|
|
|
func.func private @bar(!fir.ref<!fir.array<?xf32>>)
|
|
func.func private @abort()
|