llvm-project/flang/test/Transforms/stack-arrays.fir
Susan Tan (ス-ザン タン) bf3b704c60
[flang][NFC] Characterize allocation based on MemAlloc effect instead of pattern matching (#166806)
Flang alias analysis used to find allocation site by pattern matching
allocation ops in mainly FIR dialect. This MR extends the
characterization to instead characterize based on whether the result of
an op has MemAlloc effect.
2025-11-10 17:33:43 -05:00

500 lines
19 KiB
Plaintext

// RUN: fir-opt --stack-arrays %s | FileCheck %s
// Simplest transformation
func.func @simple() {
%0 = fir.allocmem !fir.array<42xi32>
%c0_s = arith.constant 0 : index
%c0_i32_s = arith.constant 0 : i32
%ref_s = fir.convert %0 : (!fir.heap<!fir.array<42xi32>>) -> !fir.ref<!fir.array<42xi32>>
%elt_s = fir.coordinate_of %ref_s, %c0_s : (!fir.ref<!fir.array<42xi32>>, index) -> !fir.ref<i32>
fir.store %c0_i32_s to %elt_s : !fir.ref<i32>
fir.freemem %0 : !fir.heap<!fir.array<42xi32>>
return
}
// CHECK: func.func @simple()
// CHECK: fir.alloca !fir.array<42xi32>
// CHECK: return
// Check fir.must_be_heap allocations are not moved
func.func @must_be_heap() {
%0 = fir.allocmem !fir.array<42xi32> {fir.must_be_heap = true}
fir.freemem %0 : !fir.heap<!fir.array<42xi32>>
return
}
// CHECK-LABEL: func.func @must_be_heap()
// CHECK-NEXT: %[[ALLOC:.*]] = fir.allocmem !fir.array<42xi32> {fir.must_be_heap = true}
// CHECK-NEXT: fir.freemem %[[ALLOC]] : !fir.heap<!fir.array<42xi32>>
// CHECK-NEXT: return
// CHECK-NEXT: }
// Check the data-flow-analysis can detect cases where we aren't sure if memory
// is freed by the end of the function
func.func @dfa1(%arg0: !fir.ref<!fir.logical<4>> {fir.bindc_name = "cond"}) {
%7 = arith.constant 42 : index
%8 = fir.allocmem !fir.array<?xi32>, %7 {uniq_name = "_QFdfa1Earr.alloc"}
%9 = fir.load %arg0 : !fir.ref<!fir.logical<4>>
%10 = fir.convert %9 : (!fir.logical<4>) -> i1
fir.if %10 {
fir.freemem %8 : !fir.heap<!fir.array<?xi32>>
} else {
}
return
}
// CHECK-LABEL: func.func @dfa1(%arg0: !fir.ref<!fir.logical<4>> {fir.bindc_name = "cond"})
// CHECK-NEXT: %[[C42:.*]] = arith.constant 42 : index
// CHECK-NEXT: %[[MEM:.*]] = fir.allocmem !fir.array<?xi32>, %[[C42]] {uniq_name = "_QFdfa1Earr.alloc"}
// CHECK-NEXT: %[[LOGICAL:.*]] = fir.load %arg0 : !fir.ref<!fir.logical<4>>
// CHECK-NEXT: %[[BOOL:.*]] = fir.convert %[[LOGICAL]] : (!fir.logical<4>) -> i1
// CHECK-NEXT: fir.if %[[BOOL]] {
// CHECK-NEXT: fir.freemem %[[MEM]] : !fir.heap<!fir.array<?xi32>>
// CHECK-NEXT: } else {
// CHECK-NEXT: }
// CHECK-NEXT: return
// CHECK-NEXT: }
// Check scf.if
func.func @dfa2(%arg0: i1) {
%a = fir.allocmem !fir.array<1xi8>
scf.if %arg0 {
fir.freemem %a : !fir.heap<!fir.array<1xi8>>
} else {
}
return
}
// CHECK-LABEL: func.func @dfa2(%arg0: i1)
// CHECK-NEXT: %[[MEM:.*]] = fir.allocmem !fir.array<1xi8>
// CHECK-NEXT: scf.if %arg0 {
// CHECK-NEXT: fir.freemem %[[MEM]] : !fir.heap<!fir.array<1xi8>>
// CHECK-NEXT: } else {
// CHECK-NEXT: }
// CHECK-NEXT: return
// CHECK-NEXT: }
// Check freemem in both regions
func.func @dfa3(%arg0: i1) {
%a = fir.allocmem !fir.array<1xi8>
fir.if %arg0 {
fir.freemem %a : !fir.heap<!fir.array<1xi8>>
} else {
fir.freemem %a : !fir.heap<!fir.array<1xi8>>
}
%c0_d3 = arith.constant 0 : index
%c0_i8_d3 = arith.constant 0 : i8
%ref_d3 = fir.convert %a : (!fir.heap<!fir.array<1xi8>>) -> !fir.ref<!fir.array<1xi8>>
%elt_d3 = fir.coordinate_of %ref_d3, %c0_d3 : (!fir.ref<!fir.array<1xi8>>, index) -> !fir.ref<i8>
fir.store %c0_i8_d3 to %elt_d3 : !fir.ref<i8>
return
}
// CHECK: func.func @dfa3(%arg0: i1)
// CHECK: %[[MEM:.*]] = fir.alloca !fir.array<1xi8>
// CHECK: return
func.func private @dfa3a_foo(!fir.ref<!fir.array<1xi8>>) -> ()
func.func private @dfa3a_bar(!fir.ref<!fir.array<1xi8>>) -> ()
// Check freemem in both regions, with other uses
func.func @dfa3a(%arg0: i1) {
%a = fir.allocmem !fir.array<1xi8>
fir.if %arg0 {
%ref = fir.convert %a : (!fir.heap<!fir.array<1xi8>>) -> !fir.ref<!fir.array<1xi8>>
func.call @dfa3a_foo(%ref) : (!fir.ref<!fir.array<1xi8>>) -> ()
fir.freemem %a : !fir.heap<!fir.array<1xi8>>
} else {
%ref = fir.convert %a : (!fir.heap<!fir.array<1xi8>>) -> !fir.ref<!fir.array<1xi8>>
func.call @dfa3a_bar(%ref) : (!fir.ref<!fir.array<1xi8>>) -> ()
fir.freemem %a : !fir.heap<!fir.array<1xi8>>
}
return
}
// CHECK-LABEL: func.func @dfa3a(%arg0: i1)
// CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<1xi8>
// CHECK-NEXT: %[[HEAP:.*]] = fir.convert %[[MEM]] : (!fir.ref<!fir.array<1xi8>>) -> !fir.heap<!fir.array<1xi8>>
// CHECK-NEXT: fir.if %arg0 {
// CHECK-NEXT: %[[REF:.*]] = fir.convert %[[HEAP]] : (!fir.heap<!fir.array<1xi8>>) -> !fir.ref<!fir.array<1xi8>>
// CHECK-NEXT: func.call @dfa3a_foo(%[[REF]])
// CHECK-NEXT: } else {
// CHECK-NEXT: %[[REF:.*]] = fir.convert %[[HEAP]] : (!fir.heap<!fir.array<1xi8>>) -> !fir.ref<!fir.array<1xi8>>
// CHECK-NEXT: func.call @dfa3a_bar(%[[REF]])
// CHECK-NEXT: }
// CHECK-NEXT: return
// CHECK-NEXT: }
// check the alloca is placed after all operands become available
func.func @placement1() {
// do some stuff with other ssa values
%1 = arith.constant 1 : index
%2 = arith.constant 2 : index
%3 = arith.addi %1, %2 : index
// operand is now available
%4 = fir.allocmem !fir.array<?xi32>, %3
// ...
%c0 = arith.constant 0 : index
%c0_i32 = arith.constant 0 : i32
%ref1 = fir.convert %4 : (!fir.heap<!fir.array<?xi32>>) -> !fir.ref<!fir.array<?xi32>>
%elt1 = fir.coordinate_of %ref1, %c0 : (!fir.ref<!fir.array<?xi32>>, index) -> !fir.ref<i32>
fir.store %c0_i32 to %elt1 : !fir.ref<i32>
fir.freemem %4 : !fir.heap<!fir.array<?xi32>>
return
}
// CHECK-LABEL: func.func @placement1()
// CHECK-NEXT: %[[ARG:.*]] = arith.constant 3 : index
// CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<?xi32>, %[[ARG]]
// CHECK: return
// CHECK-NEXT: }
// check that if there are no operands, then the alloca is placed early
func.func @placement2() {
// do some stuff with other ssa values
%1 = arith.constant 1 : index
%2 = arith.constant 2 : index
%3 = arith.addi %1, %2 : index
%4 = fir.allocmem !fir.array<42xi32>
// ...
%c0_p2 = arith.constant 0 : index
%c0_i32_p2 = arith.constant 0 : i32
%ref_p2 = fir.convert %4 : (!fir.heap<!fir.array<42xi32>>) -> !fir.ref<!fir.array<42xi32>>
%elt_p2 = fir.coordinate_of %ref_p2, %c0_p2 : (!fir.ref<!fir.array<42xi32>>, index) -> !fir.ref<i32>
fir.store %c0_i32_p2 to %elt_p2 : !fir.ref<i32>
fir.freemem %4 : !fir.heap<!fir.array<42xi32>>
return
}
// CHECK-LABEL: func.func @placement2()
// CHECK: %[[MEM:.*]] = fir.alloca !fir.array<42xi32>
// CHECK: %[[ONE:.*]] = arith.constant 1 : index
// CHECK: %[[TWO:.*]] = arith.constant 2 : index
// CHECK: %[[SUM:.*]] = arith.addi %[[ONE]], %[[TWO]] : index
// CHECK: return
// CHECK: }
// check that stack allocations which must be placed in loops use stacksave
func.func @placement3() {
%c1 = arith.constant 1 : index
%c1_i32 = fir.convert %c1 : (index) -> i32
%c2 = arith.constant 2 : index
%c10 = arith.constant 10 : index
%0:2 = fir.do_loop %arg0 = %c1 to %c10 step %c1 iter_args(%arg1 = %c1_i32) -> (index, i32) {
%3 = arith.addi %c1, %c2 : index
// operand is now available
%4 = fir.allocmem !fir.array<?xi32>, %3
// ...
%c0 = arith.constant 0 : index
%c0_i32 = arith.constant 0 : i32
%ref2 = fir.convert %4 : (!fir.heap<!fir.array<?xi32>>) -> !fir.ref<!fir.array<?xi32>>
%elt2 = fir.coordinate_of %ref2, %c0 : (!fir.ref<!fir.array<?xi32>>, index) -> !fir.ref<i32>
fir.store %c0_i32 to %elt2 : !fir.ref<i32>
fir.freemem %4 : !fir.heap<!fir.array<?xi32>>
fir.result %3, %c1_i32 : index, i32
}
return
}
// CHECK-LABEL: func.func @placement3()
// CHECK-NEXT: %[[C1:.*]] = arith.constant 1 : index
// CHECK-NEXT: %[[C1_I32:.*]] = fir.convert %[[C1]] : (index) -> i32
// CHECK-NEXT: %[[C2:.*]] = arith.constant 2 : index
// CHECK-NEXT: %[[C10:.*]] = arith.constant 10 : index
// CHECK-NEXT: fir.do_loop
// CHECK-NEXT: %[[SUM:.*]] = arith.addi %[[C1]], %[[C2]] : index
// CHECK-NEXT: %[[SP:.*]] = llvm.intr.stacksave : !llvm.ptr
// CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<?xi32>, %[[SUM]]
// CHECK: llvm.intr.stackrestore %[[SP]] : !llvm.ptr
// CHECK-NEXT: fir.result
// CHECK-NEXT: }
// CHECK-NEXT: return
// CHECK-NEXT: }
// check that stack save/restore are used in CFG loops
func.func @placement4(%arg0 : i1) {
%c1 = arith.constant 1 : index
%c1_i32 = fir.convert %c1 : (index) -> i32
%c2 = arith.constant 2 : index
%c10 = arith.constant 10 : index
cf.br ^bb1
^bb1:
%3 = arith.addi %c1, %c2 : index
// operand is now available
%4 = fir.allocmem !fir.array<?xi32>, %3
// ...
%c0 = arith.constant 0 : index
%c0_i32 = arith.constant 0 : i32
%ref3 = fir.convert %4 : (!fir.heap<!fir.array<?xi32>>) -> !fir.ref<!fir.array<?xi32>>
%elt3 = fir.coordinate_of %ref3, %c0 : (!fir.ref<!fir.array<?xi32>>, index) -> !fir.ref<i32>
fir.store %c0_i32 to %elt3 : !fir.ref<i32>
fir.freemem %4 : !fir.heap<!fir.array<?xi32>>
cf.cond_br %arg0, ^bb1, ^bb2
^bb2:
return
}
// CHECK-LABEL: func.func @placement4(%arg0: i1)
// CHECK-NEXT: %[[C1:.*]] = arith.constant 1 : index
// CHECK-NEXT: %[[C1_I32:.*]] = fir.convert %[[C1]] : (index) -> i32
// CHECK-NEXT: %[[C10:.*]] = arith.constant 10 : index
// CHECK-NEXT: cf.br ^bb1
// CHECK-NEXT: ^bb1:
// CHECK-NEXT: %[[C3:.*]] = arith.constant 3 : index
// CHECK-NEXT: %[[SP:.*]] = llvm.intr.stacksave : !llvm.ptr
// CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<?xi32>, %[[C3]]
// CHECK: llvm.intr.stackrestore %[[SP]] : !llvm.ptr
// CHECK-NEXT: cf.cond_br %arg0, ^bb1, ^bb2
// CHECK-NEXT: ^bb2:
// CHECK-NEXT: return
// CHECK-NEXT: }
// check that stacksave is not used when there is an intervening alloca
func.func @placement5() {
%c1 = arith.constant 1 : index
%c1_i32 = fir.convert %c1 : (index) -> i32
%c2 = arith.constant 2 : index
%c10 = arith.constant 10 : index
%0:2 = fir.do_loop %arg0 = %c1 to %c10 step %c1 iter_args(%arg1 = %c1_i32) -> (index, i32) {
%3 = arith.addi %c1, %c2 : index
// operand is now available
%4 = fir.allocmem !fir.array<?xi32>, %3
%5 = fir.alloca i32
fir.freemem %4 : !fir.heap<!fir.array<?xi32>>
fir.result %3, %c1_i32 : index, i32
}
return
}
// CHECK-LABEL: func.func @placement5()
// CHECK-NEXT: %[[C1:.*]] = arith.constant 1 : index
// CHECK-NEXT: %[[C1_I32:.*]] = fir.convert %[[C1]] : (index) -> i32
// CHECK-NEXT: %[[C2:.*]] = arith.constant 2 : index
// CHECK-NEXT: %[[C10:.*]] = arith.constant 10 : index
// CHECK-NEXT: fir.do_loop
// CHECK-NEXT: %[[SUM:.*]] = arith.addi %[[C1]], %[[C2]] : index
// CHECK-NEXT: %[[MEM:.*]] = fir.allocmem !fir.array<?xi32>, %[[SUM]]
// CHECK-NEXT: %[[IDX:.*]] = fir.alloca i32
// CHECK-NEXT: fir.freemem %[[MEM]] : !fir.heap<!fir.array<?xi32>>
// CHECK-NEXT: fir.result
// CHECK-NEXT: }
// CHECK-NEXT: return
// CHECK-NEXT: }
// check that stack save/restore are not used when the memalloc and freemem are
// in different blocks
func.func @placement6(%arg0: i1) {
%c1 = arith.constant 1 : index
%c1_i32 = fir.convert %c1 : (index) -> i32
%c2 = arith.constant 2 : index
%c10 = arith.constant 10 : index
cf.br ^bb1
^bb1:
%3 = arith.addi %c1, %c2 : index
// operand is now available
%4 = fir.allocmem !fir.array<?xi32>, %3
// ...
cf.cond_br %arg0, ^bb2, ^bb3
^bb2:
// ...
fir.freemem %4 : !fir.heap<!fir.array<?xi32>>
cf.br ^bb1
^bb3:
// ...
fir.freemem %4 : !fir.heap<!fir.array<?xi32>>
cf.br ^bb1
}
// CHECK-LABEL: func.func @placement6(%arg0: i1)
// CHECK-NEXT: %[[c1:.*]] = arith.constant 1 : index
// CHECK-NEXT: %[[c1_i32:.*]] = fir.convert %[[c1]] : (index) -> i32
// CHECK-NEXT: %[[c2:.*]] = arith.constant 2 : index
// CHECK-NEXT: %[[c10:.*]] = arith.constant 10 : index
// CHECK-NEXT: cf.br ^bb1
// CHECK-NEXT: ^bb1:
// CHECK-NEXT: %[[ADD:.*]] = arith.addi %[[c1]], %[[c2]] : index
// CHECK-NEXT: %[[MEM:.*]] = fir.allocmem !fir.array<?xi32>, %[[ADD]]
// CHECK-NEXT: cf.cond_br %arg0, ^bb2, ^bb3
// CHECK-NEXT: ^bb2:
// CHECK-NEXT: fir.freemem %[[MEM]] : !fir.heap<!fir.array<?xi32>>
// CHECK-NEXT: cf.br ^bb1
// CHECK-NEXT: ^bb3:
// CHECK-NEXT: fir.freemem %[[MEM]] : !fir.heap<!fir.array<?xi32>>
// CHECK-NEXT: cf.br ^bb1
// CHECK-NEXT: }
// Check multiple returns, where the memory is always freed
func.func @returns(%arg0: i1) {
%0 = fir.allocmem !fir.array<42xi32>
%c0_ret = arith.constant 0 : index
%c0_i32_ret = arith.constant 0 : i32
%ref_ret = fir.convert %0 : (!fir.heap<!fir.array<42xi32>>) -> !fir.ref<!fir.array<42xi32>>
%elt_ret = fir.coordinate_of %ref_ret, %c0_ret : (!fir.ref<!fir.array<42xi32>>, index) -> !fir.ref<i32>
fir.store %c0_i32_ret to %elt_ret : !fir.ref<i32>
cf.cond_br %arg0, ^bb1, ^bb2
^bb1:
fir.freemem %0 : !fir.heap<!fir.array<42xi32>>
return
^bb2:
fir.freemem %0 : !fir.heap<!fir.array<42xi32>>
return
}
// CHECK-LABEL: func.func @returns(
// CHECK: %[[ALLOC:.*]] = fir.alloca !fir.array<42xi32>
// CHECK: cf.cond_br %{{.*}}, ^bb1, ^bb2
// CHECK-NEXT: ^bb1:
// CHECK-NEXT: return
// CHECK-NEXT: ^bb2:
// CHECK-NEXT: return
// CHECK-NEXT: }
// Check multiple returns, where the memory is not freed on one branch
func.func @returns2(%arg0: i1) {
%0 = fir.allocmem !fir.array<42xi32>
%c0_ret2 = arith.constant 0 : index
%c0_i32_ret2 = arith.constant 0 : i32
%ref_ret2 = fir.convert %0 : (!fir.heap<!fir.array<42xi32>>) -> !fir.ref<!fir.array<42xi32>>
%elt_ret2 = fir.coordinate_of %ref_ret2, %c0_ret2 : (!fir.ref<!fir.array<42xi32>>, index) -> !fir.ref<i32>
fir.store %c0_i32_ret2 to %elt_ret2 : !fir.ref<i32>
cf.cond_br %arg0, ^bb1, ^bb2
^bb1:
fir.freemem %0 : !fir.heap<!fir.array<42xi32>>
return
^bb2:
return
}
// CHECK-LABEL: func.func @returns2(
// CHECK: %[[ALLOC:.*]] = fir.allocmem !fir.array<42xi32>
// CHECK: cf.cond_br %{{.*}}, ^bb1, ^bb2
// CHECK-NEXT: ^bb1:
// CHECK-NEXT: fir.freemem %[[ALLOC]] : !fir.heap<!fir.array<42xi32>>
// CHECK-NEXT: return
// CHECK-NEXT: ^bb2:
// CHECK-NEXT: return
// CHECK-NEXT: }
// Check allocations are not moved outside of an omp region
func.func @omp_placement1() {
omp.sections {
omp.section {
%mem = fir.allocmem !fir.array<42xi32>
fir.freemem %mem : !fir.heap<!fir.array<42xi32>>
omp.terminator
}
omp.terminator
}
return
}
// CHECK-LABEL: func.func @omp_placement1()
// CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<42xi32>
// CHECK-NEXT: %[[MEM_CONV:.*]] = fir.convert %[[MEM]] : (!fir.ref<!fir.array<42xi32>>) -> !fir.heap<!fir.array<42xi32>>
// CHECK-NEXT: omp.sections {
// CHECK-NEXT: omp.section {
// CHECK-NEXT: omp.terminator
// CHECK-NEXT: }
// CHECK-NEXT: omp.terminator
// CHECK-NEXT: }
// CHECK-NEXT: return
// CHECK-NEXT: }
// function terminated by stop statement
func.func @stop_terminator() {
%0 = fir.allocmem !fir.array<42xi32>
%c0 = arith.constant 0 : index
%c0_i32_st = arith.constant 0 : i32
%ref4 = fir.convert %0 : (!fir.heap<!fir.array<42xi32>>) -> !fir.ref<!fir.array<42xi32>>
%elt4 = fir.coordinate_of %ref4, %c0 : (!fir.ref<!fir.array<42xi32>>, index) -> !fir.ref<i32>
fir.store %c0_i32_st to %elt4 : !fir.ref<i32>
fir.freemem %0 : !fir.heap<!fir.array<42xi32>>
%c0_i32 = arith.constant 0 : i32
%false = arith.constant false
fir.call @_FortranAStopStatement(%c0_i32, %false, %false) : (i32, i1, i1) -> ()
fir.unreachable
}
// CHECK-LABEL: func.func @stop_terminator()
// CHECK: fir.alloca !fir.array<42xi32>
// CHECK: fir.call @_FortranAStopStatement(
// CHECK: fir.unreachable
// check that stack allocations that use fir.declare which must be placed in loops
// use stacksave
func.func @placement_loop_declare() {
%c1 = arith.constant 1 : index
%c1_i32 = fir.convert %c1 : (index) -> i32
%c2 = arith.constant 2 : index
%c10 = arith.constant 10 : index
%0:2 = fir.do_loop %arg0 = %c1 to %c10 step %c1 iter_args(%arg1 = %c1_i32) -> (index, i32) {
%3 = arith.addi %c1, %c2 : index
// operand is now available
%4 = fir.allocmem !fir.array<?xi32>, %3
%shape = fir.shape %3 : (index) -> !fir.shape<1>
%5 = fir.declare %4(%shape) {uniq_name = "temp"} : (!fir.heap<!fir.array<?xi32>>, !fir.shape<1>) -> !fir.heap<!fir.array<?xi32>>
// ...
fir.freemem %5 : !fir.heap<!fir.array<?xi32>>
fir.result %3, %c1_i32 : index, i32
}
return
}
// CHECK-LABEL: func.func @placement_loop_declare()
// CHECK-NEXT: %[[C1:.*]] = arith.constant 1 : index
// CHECK-NEXT: %[[C1_I32:.*]] = fir.convert %[[C1]] : (index) -> i32
// CHECK-NEXT: %[[C2:.*]] = arith.constant 2 : index
// CHECK-NEXT: %[[C10:.*]] = arith.constant 10 : index
// CHECK-NEXT: fir.do_loop
// CHECK-NEXT: %[[SUM:.*]] = arith.addi %[[C1]], %[[C2]] : index
// CHECK-NEXT: %[[SP:.*]] = llvm.intr.stacksave : !llvm.ptr
// CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<?xi32>, %[[SUM]]
// CHECK: llvm.intr.stackrestore %[[SP]] : !llvm.ptr
// CHECK-NEXT: fir.result
// CHECK-NEXT: }
// CHECK-NEXT: return
// CHECK-NEXT: }
// Can we look through fir.convert and fir.declare?
func.func @lookthrough() {
%0 = fir.allocmem !fir.array<42xi32>
%c42 = arith.constant 42 : index
%shape = fir.shape %c42 : (index) -> !fir.shape<1>
%1 = fir.declare %0(%shape) {uniq_name = "name"} : (!fir.heap<!fir.array<42xi32>>, !fir.shape<1>) -> !fir.heap<!fir.array<42xi32>>
%2 = fir.convert %1 : (!fir.heap<!fir.array<42xi32>>) -> !fir.ref<!fir.array<42xi32>>
// use the ref so the converts aren't folded
%3 = fir.load %2 : !fir.ref<!fir.array<42xi32>>
%4 = fir.convert %2 : (!fir.ref<!fir.array<42xi32>>) -> !fir.heap<!fir.array<42xi32>>
fir.freemem %4 : !fir.heap<!fir.array<42xi32>>
return
}
// CHECK-LABEL: func.func @lookthrough()
// CHECK: fir.alloca !fir.array<42xi32>
// CHECK-NOT: fir.freemem
// StackArrays is better to find fir.freemem ops corresponding to fir.allocmem
// using the same look through mechanism as during the allocation analysis,
// looking through fir.convert and fir.declare.
func.func @finding_freemem_in_block() {
%c0 = arith.constant 0 : index
%c10_i32 = arith.constant 10 : i32
%c1_i32 = arith.constant 1 : i32
%0 = fir.alloca i32 {bindc_name = "k", uniq_name = "k"}
%1 = fir.declare %0 {uniq_name = "k"} : (!fir.ref<i32>) -> !fir.ref<i32>
fir.store %c1_i32 to %1 : !fir.ref<i32>
cf.br ^bb1
^bb1: // 2 preds: ^bb0, ^bb2
%2 = fir.load %1 : !fir.ref<i32>
%3 = arith.cmpi sle, %2, %c10_i32 : i32
cf.cond_br %3, ^bb2, ^bb3
^bb2: // pred: ^bb1
%4 = fir.declare %1 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "x"} : (!fir.ref<i32>) -> !fir.ref<i32>
%5 = fir.load %4 : !fir.ref<i32>
%6 = fir.convert %5 : (i32) -> index
%7 = arith.cmpi sgt, %6, %c0 : index
%8 = arith.select %7, %6, %c0 : index
%9 = fir.shape %8 : (index) -> !fir.shape<1>
%10 = fir.allocmem !fir.array<?xi32>, %8 {bindc_name = ".tmp.expr_result", uniq_name = ""}
%11 = fir.convert %10 : (!fir.heap<!fir.array<?xi32>>) -> !fir.ref<!fir.array<?xi32>>
%12 = fir.declare %11(%9) {uniq_name = ".tmp.expr_result"} : (!fir.ref<!fir.array<?xi32>>, !fir.shape<1>) -> !fir.ref<!fir.array<?xi32>>
%13 = fir.embox %12(%9) : (!fir.ref<!fir.array<?xi32>>, !fir.shape<1>) -> !fir.box<!fir.array<?xi32>>
%14 = fir.call @_QPfunc(%1) fastmath<fast> : (!fir.ref<i32>) -> !fir.array<?xi32>
fir.save_result %14 to %12(%9) : !fir.array<?xi32>, !fir.ref<!fir.array<?xi32>>, !fir.shape<1>
fir.call @_QPsub(%13) fastmath<fast> : (!fir.box<!fir.array<?xi32>>) -> ()
%15 = fir.convert %12 : (!fir.ref<!fir.array<?xi32>>) -> !fir.heap<!fir.array<?xi32>>
fir.freemem %15 : !fir.heap<!fir.array<?xi32>>
%16 = fir.load %1 : !fir.ref<i32>
%17 = arith.addi %16, %c1_i32 : i32
fir.store %17 to %1 : !fir.ref<i32>
cf.br ^bb1
^bb3: // pred: ^bb1
return
}
// CHECK-LABEL: func.func @finding_freemem_in_block()
// CHECK: fir.alloca !fir.array<?xi32>
// CHECK-NOT: fir.freemem