1 // RUN: fir-opt --stack-arrays %s | FileCheck %s
3 // Simplest transformation
5 %0 = fir.allocmem !fir.array<42xi32>
6 fir.freemem %0 : !fir.heap<!fir.array<42xi32>>
9 // CHECK: func.func @simple() {
10 // CHECK-NEXT: fir.alloca !fir.array<42xi32>
14 // Check fir.must_be_heap allocations are not moved
15 func.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>>
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>>
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
28 func.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
34 fir.freemem %8 : !fir.heap<!fir.array<?xi32>>
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 {
52 func.func @dfa2(%arg0: i1) {
53 %a = fir.allocmem !fir.array<1xi8>
55 fir.freemem %a : !fir.heap<!fir.array<1xi8>>
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 {
69 // Check freemem in both regions
70 func.func @dfa3(%arg0: i1) {
71 %a = fir.allocmem !fir.array<1xi8>
73 fir.freemem %a : !fir.heap<!fir.array<1xi8>>
75 fir.freemem %a : !fir.heap<!fir.array<1xi8>>
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 {
87 func.func private @dfa3a_foo(!fir.ref<!fir.array<1xi8>>) -> ()
88 func.func private @dfa3a_bar(!fir.ref<!fir.array<1xi8>>) -> ()
90 // Check freemem in both regions, with other uses
91 func.func @dfa3a(%arg0: i1) {
92 %a = fir.allocmem !fir.array<1xi8>
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>>
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>>
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]])
114 // CHECK-NEXT: return
117 // check the alloca is placed after all operands become available
118 func.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
126 fir.freemem %4 : !fir.heap<!fir.array<?xi32>>
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
135 // check that if there are no operands, then the alloca is placed early
136 func.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>
143 fir.freemem %4 : !fir.heap<!fir.array<42xi32>>
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
154 // check that stack allocations which must be placed in loops use stacksave
155 func.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
165 fir.freemem %4 : !fir.heap<!fir.array<?xi32>>
166 fir.result %3, %c1_i32 : index, i32
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
182 // CHECK-NEXT: return
185 // check that stack save/restore are used in CFG loops
186 func.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
193 %3 = arith.addi %c1, %c2 : index
194 // operand is now available
195 %4 = fir.allocmem !fir.array<?xi32>, %3
197 fir.freemem %4 : !fir.heap<!fir.array<?xi32>>
198 cf.cond_br %arg0, ^bb1, ^bb2
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
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
214 // CHECK-NEXT: return
217 // check that stacksave is not used when there is an intervening alloca
218 func.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
228 fir.freemem %4 : !fir.heap<!fir.array<?xi32>>
229 fir.result %3, %c1_i32 : index, i32
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
245 // CHECK-NEXT: return
248 // check that stack save/restore are not used when the memalloc and freemem are
249 // in different blocks
250 func.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
257 %3 = arith.addi %c1, %c2 : index
258 // operand is now available
259 %4 = fir.allocmem !fir.array<?xi32>, %3
261 cf.cond_br %arg0, ^bb2, ^bb3
264 fir.freemem %4 : !fir.heap<!fir.array<?xi32>>
268 fir.freemem %4 : !fir.heap<!fir.array<?xi32>>
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
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
282 // CHECK-NEXT: fir.freemem %[[MEM]] : !fir.heap<!fir.array<?xi32>>
283 // CHECK-NEXT: cf.br ^bb1
285 // CHECK-NEXT: fir.freemem %[[MEM]] : !fir.heap<!fir.array<?xi32>>
286 // CHECK-NEXT: cf.br ^bb1
289 // Check multiple returns, where the memory is always freed
290 func.func @returns(%arg0: i1) {
291 %0 = fir.allocmem !fir.array<42xi32>
292 cf.cond_br %arg0, ^bb1, ^bb2
294 fir.freemem %0 : !fir.heap<!fir.array<42xi32>>
297 fir.freemem %0 : !fir.heap<!fir.array<42xi32>>
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
304 // CHECK-NEXT: return
306 // CHECK-NEXT: return
309 // Check multiple returns, where the memory is not freed on one branch
310 func.func @returns2(%arg0: i1) {
311 %0 = fir.allocmem !fir.array<42xi32>
312 cf.cond_br %arg0, ^bb1, ^bb2
314 fir.freemem %0 : !fir.heap<!fir.array<42xi32>>
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
323 // CHECK-NEXT: fir.freemem %[[ALLOC]] : !fir.heap<!fir.array<42xi32>>
324 // CHECK-NEXT: return
326 // CHECK-NEXT: return
329 // Check allocations are not moved outside of an omp region
330 func.func @omp_placement1() {
333 %mem = fir.allocmem !fir.array<42xi32>
334 fir.freemem %mem : !fir.heap<!fir.array<42xi32>>
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
348 // CHECK-NEXT: omp.terminator
350 // CHECK-NEXT: return
353 // function terminated by stop statement
354 func.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 %none = fir.call @_FortranAStopStatement(%c0_i32, %false, %false) : (i32, i1, i1) -> none
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: %[[NONE:.*]] = fir.call @_FortranAStopStatement(%[[ZERO]], %[[FALSE]], %[[FALSE]]) : (i32, i1, i1) -> none
367 // CHECK-NEXT: fir.unreachable
371 // check that stack allocations that use fir.declare which must be placed in loops
373 func.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 %5 = fir.declare %4 {uniq_name = "temp"} : (!fir.heap<!fir.array<?xi32>>) -> !fir.heap<!fir.array<?xi32>>
384 fir.freemem %5 : !fir.heap<!fir.array<?xi32>>
385 fir.result %3, %c1_i32 : index, i32
389 // CHECK: func.func @placement_loop_declare() {
390 // CHECK-NEXT: %[[C1:.*]] = arith.constant 1 : index
391 // CHECK-NEXT: %[[C1_I32:.*]] = fir.convert %[[C1]] : (index) -> i32
392 // CHECK-NEXT: %[[C2:.*]] = arith.constant 2 : index
393 // CHECK-NEXT: %[[C10:.*]] = arith.constant 10 : index
394 // CHECK-NEXT: fir.do_loop
395 // CHECK-NEXT: %[[SUM:.*]] = arith.addi %[[C1]], %[[C2]] : index
396 // CHECK-NEXT: %[[SP:.*]] = llvm.intr.stacksave : !llvm.ptr
397 // CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<?xi32>, %[[SUM]]
398 // CHECK: llvm.intr.stackrestore %[[SP]] : !llvm.ptr
399 // CHECK-NEXT: fir.result
401 // CHECK-NEXT: return