1// RUN: fir-opt --stack-arrays %s | FileCheck %s 2 3// Simplest transformation 4func.func @simple() { 5 %0 = fir.allocmem !fir.array<42xi32> 6 fir.freemem %0 : !fir.heap<!fir.array<42xi32>> 7 return 8} 9// CHECK: func.func @simple() { 10// CHECK-NEXT: fir.alloca !fir.array<42xi32> 11// CHECK-NEXT: return 12// CHECK-NEXT: } 13 14// Check fir.must_be_heap allocations are not moved 15func.func @must_be_heap() { 16 %0 = fir.allocmem !fir.array<42xi32> {fir.must_be_heap = true} 17 fir.freemem %0 : !fir.heap<!fir.array<42xi32>> 18 return 19} 20// CHECK: func.func @must_be_heap() { 21// CHECK-NEXT: %[[ALLOC:.*]] = fir.allocmem !fir.array<42xi32> {fir.must_be_heap = true} 22// CHECK-NEXT: fir.freemem %[[ALLOC]] : !fir.heap<!fir.array<42xi32>> 23// CHECK-NEXT: return 24// CHECK-NEXT: } 25 26// Check the data-flow-analysis can detect cases where we aren't sure if memory 27// is freed by the end of the function 28func.func @dfa1(%arg0: !fir.ref<!fir.logical<4>> {fir.bindc_name = "cond"}) { 29 %7 = arith.constant 42 : index 30 %8 = fir.allocmem !fir.array<?xi32>, %7 {uniq_name = "_QFdfa1Earr.alloc"} 31 %9 = fir.load %arg0 : !fir.ref<!fir.logical<4>> 32 %10 = fir.convert %9 : (!fir.logical<4>) -> i1 33 fir.if %10 { 34 fir.freemem %8 : !fir.heap<!fir.array<?xi32>> 35 } else { 36 } 37 return 38} 39// CHECK: func.func @dfa1(%arg0: !fir.ref<!fir.logical<4>> {fir.bindc_name = "cond"}) { 40// CHECK-NEXT: %[[C42:.*]] = arith.constant 42 : index 41// CHECK-NEXT: %[[MEM:.*]] = fir.allocmem !fir.array<?xi32>, %[[C42]] {uniq_name = "_QFdfa1Earr.alloc"} 42// CHECK-NEXT: %[[LOGICAL:.*]] = fir.load %arg0 : !fir.ref<!fir.logical<4>> 43// CHECK-NEXT: %[[BOOL:.*]] = fir.convert %[[LOGICAL]] : (!fir.logical<4>) -> i1 44// CHECK-NEXT: fir.if %[[BOOL]] { 45// CHECK-NEXT: fir.freemem %[[MEM]] : !fir.heap<!fir.array<?xi32>> 46// CHECK-NEXT: } else { 47// CHECK-NEXT: } 48// CHECK-NEXT: return 49// CHECK-NEXT: } 50 51// Check scf.if 52func.func @dfa2(%arg0: i1) { 53 %a = fir.allocmem !fir.array<1xi8> 54 scf.if %arg0 { 55 fir.freemem %a : !fir.heap<!fir.array<1xi8>> 56 } else { 57 } 58 return 59} 60// CHECK: func.func @dfa2(%arg0: i1) { 61// CHECK-NEXT: %[[MEM:.*]] = fir.allocmem !fir.array<1xi8> 62// CHECK-NEXT: scf.if %arg0 { 63// CHECK-NEXT: fir.freemem %[[MEM]] : !fir.heap<!fir.array<1xi8>> 64// CHECK-NEXT: } else { 65// CHECK-NEXT: } 66// CHECK-NEXT: return 67// CHECK-NEXT: } 68 69// Check freemem in both regions 70func.func @dfa3(%arg0: i1) { 71 %a = fir.allocmem !fir.array<1xi8> 72 fir.if %arg0 { 73 fir.freemem %a : !fir.heap<!fir.array<1xi8>> 74 } else { 75 fir.freemem %a : !fir.heap<!fir.array<1xi8>> 76 } 77 return 78} 79// CHECK: func.func @dfa3(%arg0: i1) { 80// CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<1xi8> 81// CHECK-NEXT: fir.if %arg0 { 82// CHECK-NEXT: } else { 83// CHECK-NEXT: } 84// CHECK-NEXT: return 85// CHECK-NEXT: } 86 87func.func private @dfa3a_foo(!fir.ref<!fir.array<1xi8>>) -> () 88func.func private @dfa3a_bar(!fir.ref<!fir.array<1xi8>>) -> () 89 90// Check freemem in both regions, with other uses 91func.func @dfa3a(%arg0: i1) { 92 %a = fir.allocmem !fir.array<1xi8> 93 fir.if %arg0 { 94 %ref = fir.convert %a : (!fir.heap<!fir.array<1xi8>>) -> !fir.ref<!fir.array<1xi8>> 95 func.call @dfa3a_foo(%ref) : (!fir.ref<!fir.array<1xi8>>) -> () 96 fir.freemem %a : !fir.heap<!fir.array<1xi8>> 97 } else { 98 %ref = fir.convert %a : (!fir.heap<!fir.array<1xi8>>) -> !fir.ref<!fir.array<1xi8>> 99 func.call @dfa3a_bar(%ref) : (!fir.ref<!fir.array<1xi8>>) -> () 100 fir.freemem %a : !fir.heap<!fir.array<1xi8>> 101 } 102 return 103} 104// CHECK: func.func @dfa3a(%arg0: i1) { 105// CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<1xi8> 106// CHECK-NEXT: %[[HEAP:.*]] = fir.convert %[[MEM]] : (!fir.ref<!fir.array<1xi8>>) -> !fir.heap<!fir.array<1xi8>> 107// CHECK-NEXT: fir.if %arg0 { 108// CHECK-NEXT: %[[REF:.*]] = fir.convert %[[HEAP]] : (!fir.heap<!fir.array<1xi8>>) -> !fir.ref<!fir.array<1xi8>> 109// CHECK-NEXT: func.call @dfa3a_foo(%[[REF]]) 110// CHECK-NEXT: } else { 111// CHECK-NEXT: %[[REF:.*]] = fir.convert %[[HEAP]] : (!fir.heap<!fir.array<1xi8>>) -> !fir.ref<!fir.array<1xi8>> 112// CHECK-NEXT: func.call @dfa3a_bar(%[[REF]]) 113// CHECK-NEXT: } 114// CHECK-NEXT: return 115// CHECK-NEXT: } 116 117// check the alloca is placed after all operands become available 118func.func @placement1() { 119 // do some stuff with other ssa values 120 %1 = arith.constant 1 : index 121 %2 = arith.constant 2 : index 122 %3 = arith.addi %1, %2 : index 123 // operand is now available 124 %4 = fir.allocmem !fir.array<?xi32>, %3 125 // ... 126 fir.freemem %4 : !fir.heap<!fir.array<?xi32>> 127 return 128} 129// CHECK: func.func @placement1() { 130// CHECK-NEXT: %[[ARG:.*]] = arith.constant 3 : index 131// CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<?xi32>, %[[ARG]] 132// CHECK-NEXT: return 133// CHECK-NEXT: } 134 135// check that if there are no operands, then the alloca is placed early 136func.func @placement2() { 137 // do some stuff with other ssa values 138 %1 = arith.constant 1 : index 139 %2 = arith.constant 2 : index 140 %3 = arith.addi %1, %2 : index 141 %4 = fir.allocmem !fir.array<42xi32> 142 // ... 143 fir.freemem %4 : !fir.heap<!fir.array<42xi32>> 144 return 145} 146// CHECK: func.func @placement2() { 147// CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<42xi32> 148// CHECK-NEXT: %[[ONE:.*]] = arith.constant 1 : index 149// CHECK-NEXT: %[[TWO:.*]] = arith.constant 2 : index 150// CHECK-NEXT: %[[SUM:.*]] = arith.addi %[[ONE]], %[[TWO]] : index 151// CHECK-NEXT: return 152// CHECK-NEXT: } 153 154// check that stack allocations which must be placed in loops use stacksave 155func.func @placement3() { 156 %c1 = arith.constant 1 : index 157 %c1_i32 = fir.convert %c1 : (index) -> i32 158 %c2 = arith.constant 2 : index 159 %c10 = arith.constant 10 : index 160 %0:2 = fir.do_loop %arg0 = %c1 to %c10 step %c1 iter_args(%arg1 = %c1_i32) -> (index, i32) { 161 %3 = arith.addi %c1, %c2 : index 162 // operand is now available 163 %4 = fir.allocmem !fir.array<?xi32>, %3 164 // ... 165 fir.freemem %4 : !fir.heap<!fir.array<?xi32>> 166 fir.result %3, %c1_i32 : index, i32 167 } 168 return 169} 170// CHECK: func.func @placement3() { 171// CHECK-NEXT: %[[C1:.*]] = arith.constant 1 : index 172// CHECK-NEXT: %[[C1_I32:.*]] = fir.convert %[[C1]] : (index) -> i32 173// CHECK-NEXT: %[[C2:.*]] = arith.constant 2 : index 174// CHECK-NEXT: %[[C10:.*]] = arith.constant 10 : index 175// CHECK-NEXT: fir.do_loop 176// CHECK-NEXT: %[[SUM:.*]] = arith.addi %[[C1]], %[[C2]] : index 177// CHECK-NEXT: %[[SP:.*]] = llvm.intr.stacksave : !llvm.ptr 178// CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<?xi32>, %[[SUM]] 179// CHECK-NEXT: llvm.intr.stackrestore %[[SP]] : !llvm.ptr 180// CHECK-NEXT: fir.result 181// CHECK-NEXT: } 182// CHECK-NEXT: return 183// CHECK-NEXT: } 184 185// check that stack save/restore are used in CFG loops 186func.func @placement4(%arg0 : i1) { 187 %c1 = arith.constant 1 : index 188 %c1_i32 = fir.convert %c1 : (index) -> i32 189 %c2 = arith.constant 2 : index 190 %c10 = arith.constant 10 : index 191 cf.br ^bb1 192^bb1: 193 %3 = arith.addi %c1, %c2 : index 194 // operand is now available 195 %4 = fir.allocmem !fir.array<?xi32>, %3 196 // ... 197 fir.freemem %4 : !fir.heap<!fir.array<?xi32>> 198 cf.cond_br %arg0, ^bb1, ^bb2 199^bb2: 200 return 201} 202// CHECK: func.func @placement4(%arg0: i1) { 203// CHECK-NEXT: %[[C1:.*]] = arith.constant 1 : index 204// CHECK-NEXT: %[[C1_I32:.*]] = fir.convert %[[C1]] : (index) -> i32 205// CHECK-NEXT: %[[C10:.*]] = arith.constant 10 : index 206// CHECK-NEXT: cf.br ^bb1 207// CHECK-NEXT: ^bb1: 208// CHECK-NEXT: %[[C3:.*]] = arith.constant 3 : index 209// CHECK-NEXT: %[[SP:.*]] = llvm.intr.stacksave : !llvm.ptr 210// CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<?xi32>, %[[C3]] 211// CHECK-NEXT: llvm.intr.stackrestore %[[SP]] : !llvm.ptr 212// CHECK-NEXT: cf.cond_br %arg0, ^bb1, ^bb2 213// CHECK-NEXT: ^bb2: 214// CHECK-NEXT: return 215// CHECK-NEXT: } 216 217// check that stacksave is not used when there is an intervening alloca 218func.func @placement5() { 219 %c1 = arith.constant 1 : index 220 %c1_i32 = fir.convert %c1 : (index) -> i32 221 %c2 = arith.constant 2 : index 222 %c10 = arith.constant 10 : index 223 %0:2 = fir.do_loop %arg0 = %c1 to %c10 step %c1 iter_args(%arg1 = %c1_i32) -> (index, i32) { 224 %3 = arith.addi %c1, %c2 : index 225 // operand is now available 226 %4 = fir.allocmem !fir.array<?xi32>, %3 227 %5 = fir.alloca i32 228 fir.freemem %4 : !fir.heap<!fir.array<?xi32>> 229 fir.result %3, %c1_i32 : index, i32 230 } 231 return 232} 233// CHECK: func.func @placement5() { 234// CHECK-NEXT: %[[C1:.*]] = arith.constant 1 : index 235// CHECK-NEXT: %[[C1_I32:.*]] = fir.convert %[[C1]] : (index) -> i32 236// CHECK-NEXT: %[[C2:.*]] = arith.constant 2 : index 237// CHECK-NEXT: %[[C10:.*]] = arith.constant 10 : index 238// CHECK-NEXT: fir.do_loop 239// CHECK-NEXT: %[[SUM:.*]] = arith.addi %[[C1]], %[[C2]] : index 240// CHECK-NEXT: %[[MEM:.*]] = fir.allocmem !fir.array<?xi32>, %[[SUM]] 241// CHECK-NEXT: %[[IDX:.*]] = fir.alloca i32 242// CHECK-NEXT: fir.freemem %[[MEM]] : !fir.heap<!fir.array<?xi32>> 243// CHECK-NEXT: fir.result 244// CHECK-NEXT: } 245// CHECK-NEXT: return 246// CHECK-NEXT: } 247 248// check that stack save/restore are not used when the memalloc and freemem are 249// in different blocks 250func.func @placement6(%arg0: i1) { 251 %c1 = arith.constant 1 : index 252 %c1_i32 = fir.convert %c1 : (index) -> i32 253 %c2 = arith.constant 2 : index 254 %c10 = arith.constant 10 : index 255 cf.br ^bb1 256^bb1: 257 %3 = arith.addi %c1, %c2 : index 258 // operand is now available 259 %4 = fir.allocmem !fir.array<?xi32>, %3 260 // ... 261 cf.cond_br %arg0, ^bb2, ^bb3 262^bb2: 263 // ... 264 fir.freemem %4 : !fir.heap<!fir.array<?xi32>> 265 cf.br ^bb1 266^bb3: 267 // ... 268 fir.freemem %4 : !fir.heap<!fir.array<?xi32>> 269 cf.br ^bb1 270} 271// CHECK: func.func @placement6(%arg0: i1) { 272// CHECK-NEXT: %[[c1:.*]] = arith.constant 1 : index 273// CHECK-NEXT: %[[c1_i32:.*]] = fir.convert %[[c1]] : (index) -> i32 274// CHECK-NEXT: %[[c2:.*]] = arith.constant 2 : index 275// CHECK-NEXT: %[[c10:.*]] = arith.constant 10 : index 276// CHECK-NEXT: cf.br ^bb1 277// CHECK-NEXT: ^bb1: 278// CHECK-NEXT: %[[ADD:.*]] = arith.addi %[[c1]], %[[c2]] : index 279// CHECK-NEXT: %[[MEM:.*]] = fir.allocmem !fir.array<?xi32>, %[[ADD]] 280// CHECK-NEXT: cf.cond_br %arg0, ^bb2, ^bb3 281// CHECK-NEXT: ^bb2: 282// CHECK-NEXT: fir.freemem %[[MEM]] : !fir.heap<!fir.array<?xi32>> 283// CHECK-NEXT: cf.br ^bb1 284// CHECK-NEXT: ^bb3: 285// CHECK-NEXT: fir.freemem %[[MEM]] : !fir.heap<!fir.array<?xi32>> 286// CHECK-NEXT: cf.br ^bb1 287// CHECK-NEXT: } 288 289// Check multiple returns, where the memory is always freed 290func.func @returns(%arg0: i1) { 291 %0 = fir.allocmem !fir.array<42xi32> 292 cf.cond_br %arg0, ^bb1, ^bb2 293^bb1: 294 fir.freemem %0 : !fir.heap<!fir.array<42xi32>> 295 return 296^bb2: 297 fir.freemem %0 : !fir.heap<!fir.array<42xi32>> 298 return 299} 300// CHECK: func.func @returns(%[[COND:.*]]: i1) { 301// CHECK-NEXT: %[[ALLOC:.*]] = fir.alloca !fir.array<42xi32> 302// CHECK-NEXT: cf.cond_br %[[COND]], ^bb1, ^bb2 303// CHECK-NEXT: ^bb1: 304// CHECK-NEXT: return 305// CHECK-NEXT: ^bb2: 306// CHECK-NEXT: return 307// CHECK-NEXT: } 308 309// Check multiple returns, where the memory is not freed on one branch 310func.func @returns2(%arg0: i1) { 311 %0 = fir.allocmem !fir.array<42xi32> 312 cf.cond_br %arg0, ^bb1, ^bb2 313^bb1: 314 fir.freemem %0 : !fir.heap<!fir.array<42xi32>> 315 return 316^bb2: 317 return 318} 319// CHECK: func.func @returns2(%[[COND:.*]]: i1) { 320// CHECK-NEXT: %[[ALLOC:.*]] = fir.allocmem !fir.array<42xi32> 321// CHECK-NEXT: cf.cond_br %[[COND]], ^bb1, ^bb2 322// CHECK-NEXT: ^bb1: 323// CHECK-NEXT: fir.freemem %[[ALLOC]] : !fir.heap<!fir.array<42xi32>> 324// CHECK-NEXT: return 325// CHECK-NEXT: ^bb2: 326// CHECK-NEXT: return 327// CHECK-NEXT: } 328 329// Check allocations are not moved outside of an omp region 330func.func @omp_placement1() { 331 omp.sections { 332 omp.section { 333 %mem = fir.allocmem !fir.array<42xi32> 334 fir.freemem %mem : !fir.heap<!fir.array<42xi32>> 335 omp.terminator 336 } 337 omp.terminator 338 } 339 return 340} 341// CHECK: func.func @omp_placement1() { 342// CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<42xi32> 343// CHECK-NEXT: %[[MEM_CONV:.*]] = fir.convert %[[MEM]] : (!fir.ref<!fir.array<42xi32>>) -> !fir.heap<!fir.array<42xi32>> 344// CHECK-NEXT: omp.sections { 345// CHECK-NEXT: omp.section { 346// CHECK-NEXT: omp.terminator 347// CHECK-NEXT: } 348// CHECK-NEXT: omp.terminator 349// CHECK-NEXT: } 350// CHECK-NEXT: return 351// CHECK-NEXT: } 352 353// function terminated by stop statement 354func.func @stop_terminator() { 355 %0 = fir.allocmem !fir.array<42xi32> 356 fir.freemem %0 : !fir.heap<!fir.array<42xi32>> 357 %c0_i32 = arith.constant 0 : i32 358 %false = arith.constant false 359 fir.call @_FortranAStopStatement(%c0_i32, %false, %false) : (i32, i1, i1) -> () 360 fir.unreachable 361} 362// CHECK: func.func @stop_terminator() { 363// CHECK-NEXT: fir.alloca !fir.array<42xi32> 364// CHECK-NEXT: %[[ZERO:.*]] = arith.constant 0 : i32 365// CHECK-NEXT: %[[FALSE:.*]] = arith.constant false 366// CHECK-NEXT: fir.call @_FortranAStopStatement(%[[ZERO]], %[[FALSE]], %[[FALSE]]) : (i32, i1, i1) -> () 367// CHECK-NEXT: fir.unreachable 368// CHECK-NEXT: } 369 370 371// check that stack allocations that use fir.declare which must be placed in loops 372// use stacksave 373func.func @placement_loop_declare() { 374 %c1 = arith.constant 1 : index 375 %c1_i32 = fir.convert %c1 : (index) -> i32 376 %c2 = arith.constant 2 : index 377 %c10 = arith.constant 10 : index 378 %0:2 = fir.do_loop %arg0 = %c1 to %c10 step %c1 iter_args(%arg1 = %c1_i32) -> (index, i32) { 379 %3 = arith.addi %c1, %c2 : index 380 // operand is now available 381 %4 = fir.allocmem !fir.array<?xi32>, %3 382 %shape = fir.shape %3 : (index) -> !fir.shape<1> 383 %5 = fir.declare %4(%shape) {uniq_name = "temp"} : (!fir.heap<!fir.array<?xi32>>, !fir.shape<1>) -> !fir.heap<!fir.array<?xi32>> 384 // ... 385 fir.freemem %5 : !fir.heap<!fir.array<?xi32>> 386 fir.result %3, %c1_i32 : index, i32 387 } 388 return 389} 390// CHECK: func.func @placement_loop_declare() { 391// CHECK-NEXT: %[[C1:.*]] = arith.constant 1 : index 392// CHECK-NEXT: %[[C1_I32:.*]] = fir.convert %[[C1]] : (index) -> i32 393// CHECK-NEXT: %[[C2:.*]] = arith.constant 2 : index 394// CHECK-NEXT: %[[C10:.*]] = arith.constant 10 : index 395// CHECK-NEXT: fir.do_loop 396// CHECK-NEXT: %[[SUM:.*]] = arith.addi %[[C1]], %[[C2]] : index 397// CHECK-NEXT: %[[SP:.*]] = llvm.intr.stacksave : !llvm.ptr 398// CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<?xi32>, %[[SUM]] 399// CHECK: llvm.intr.stackrestore %[[SP]] : !llvm.ptr 400// CHECK-NEXT: fir.result 401// CHECK-NEXT: } 402// CHECK-NEXT: return 403// CHECK-NEXT: } 404 405// Can we look through fir.convert and fir.declare? 406func.func @lookthrough() { 407 %0 = fir.allocmem !fir.array<42xi32> 408 %c42 = arith.constant 42 : index 409 %shape = fir.shape %c42 : (index) -> !fir.shape<1> 410 %1 = fir.declare %0(%shape) {uniq_name = "name"} : (!fir.heap<!fir.array<42xi32>>, !fir.shape<1>) -> !fir.heap<!fir.array<42xi32>> 411 %2 = fir.convert %1 : (!fir.heap<!fir.array<42xi32>>) -> !fir.ref<!fir.array<42xi32>> 412 // use the ref so the converts aren't folded 413 %3 = fir.load %2 : !fir.ref<!fir.array<42xi32>> 414 %4 = fir.convert %2 : (!fir.ref<!fir.array<42xi32>>) -> !fir.heap<!fir.array<42xi32>> 415 fir.freemem %4 : !fir.heap<!fir.array<42xi32>> 416 return 417} 418// CHECK: func.func @lookthrough() { 419// CHECK: fir.alloca !fir.array<42xi32> 420// CHECK-NOT: fir.freemem 421 422// StackArrays is better to find fir.freemem ops corresponding to fir.allocmem 423// using the same look through mechanism as during the allocation analysis, 424// looking through fir.convert and fir.declare. 425func.func @finding_freemem_in_block() { 426 %c0 = arith.constant 0 : index 427 %c10_i32 = arith.constant 10 : i32 428 %c1_i32 = arith.constant 1 : i32 429 %0 = fir.alloca i32 {bindc_name = "k", uniq_name = "k"} 430 %1 = fir.declare %0 {uniq_name = "k"} : (!fir.ref<i32>) -> !fir.ref<i32> 431 fir.store %c1_i32 to %1 : !fir.ref<i32> 432 cf.br ^bb1 433^bb1: // 2 preds: ^bb0, ^bb2 434 %2 = fir.load %1 : !fir.ref<i32> 435 %3 = arith.cmpi sle, %2, %c10_i32 : i32 436 cf.cond_br %3, ^bb2, ^bb3 437^bb2: // pred: ^bb1 438 %4 = fir.declare %1 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "x"} : (!fir.ref<i32>) -> !fir.ref<i32> 439 %5 = fir.load %4 : !fir.ref<i32> 440 %6 = fir.convert %5 : (i32) -> index 441 %7 = arith.cmpi sgt, %6, %c0 : index 442 %8 = arith.select %7, %6, %c0 : index 443 %9 = fir.shape %8 : (index) -> !fir.shape<1> 444 %10 = fir.allocmem !fir.array<?xi32>, %8 {bindc_name = ".tmp.expr_result", uniq_name = ""} 445 %11 = fir.convert %10 : (!fir.heap<!fir.array<?xi32>>) -> !fir.ref<!fir.array<?xi32>> 446 %12 = fir.declare %11(%9) {uniq_name = ".tmp.expr_result"} : (!fir.ref<!fir.array<?xi32>>, !fir.shape<1>) -> !fir.ref<!fir.array<?xi32>> 447 %13 = fir.embox %12(%9) : (!fir.ref<!fir.array<?xi32>>, !fir.shape<1>) -> !fir.box<!fir.array<?xi32>> 448 %14 = fir.call @_QPfunc(%1) fastmath<fast> : (!fir.ref<i32>) -> !fir.array<?xi32> 449 fir.save_result %14 to %12(%9) : !fir.array<?xi32>, !fir.ref<!fir.array<?xi32>>, !fir.shape<1> 450 fir.call @_QPsub(%13) fastmath<fast> : (!fir.box<!fir.array<?xi32>>) -> () 451 %15 = fir.convert %12 : (!fir.ref<!fir.array<?xi32>>) -> !fir.heap<!fir.array<?xi32>> 452 fir.freemem %15 : !fir.heap<!fir.array<?xi32>> 453 %16 = fir.load %1 : !fir.ref<i32> 454 %17 = arith.addi %16, %c1_i32 : i32 455 fir.store %17 to %1 : !fir.ref<i32> 456 cf.br ^bb1 457^bb3: // pred: ^bb1 458 return 459} 460// CHECK: func.func @finding_freemem_in_block() { 461// CHECK: fir.alloca !fir.array<?xi32> 462// CHECK-NOT: fir.freemem 463