[flang] Fix crash in HLFIR generation (#118399)
[llvm-project.git] / mlir / test / Transforms / promote-buffers-to-stack.mlir
blobf7f2d2ec114ca820bff1aeb3f94328ede568a1d8
1 // RUN: mlir-opt -promote-buffers-to-stack -split-input-file %s | FileCheck %s --check-prefix=CHECK --check-prefix DEFINDEX
2 // RUN: mlir-opt -promote-buffers-to-stack="max-alloc-size-in-bytes=64" -split-input-file %s | FileCheck %s --check-prefix=CHECK --check-prefix LOWLIMIT
3 // RUN: mlir-opt -promote-buffers-to-stack="max-rank-of-allocated-memref=2" -split-input-file %s | FileCheck %s --check-prefix=CHECK --check-prefix RANK
5 // This file checks the behavior of PromoteBuffersToStack pass for converting
6 // AllocOps into AllocaOps, if possible.
8 // Test Case:
9 //    bb0
10 //   /   \
11 //  bb1  bb2 <- Initial position of AllocOp
12 //   \   /
13 //    bb3
14 // PromoteBuffersToStack expected behavior: It should convert %0 into an
15 // AllocaOp.
17 // CHECK-LABEL: func @condBranch
18 func.func @condBranch(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) {
19   cf.cond_br %arg0, ^bb1, ^bb2
20 ^bb1:
21   cf.br ^bb3(%arg1 : memref<2xf32>)
22 ^bb2:
23   %0 = memref.alloc() : memref<2xf32>
24   test.buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>)
25   cf.br ^bb3(%0 : memref<2xf32>)
26 ^bb3(%1: memref<2xf32>):
27   test.copy(%1, %arg2) : (memref<2xf32>, memref<2xf32>)
28   return
31 // CHECK-NEXT: cf.cond_br {{.*}}
32 //      CHECK: ^bb2
33 // CHECK-NEXT: %[[ALLOCA:.*]] = memref.alloca()
34 //      CHECK: test.copy
35 // CHECK-NEXT: return
37 // -----
39 // Test Case:
40 //    bb0
41 //   /   \
42 //  bb1  bb2 <- Initial position of AllocOp
43 //   \   /
44 //    bb3
45 // PromoteBuffersToStack expected behavior:
46 // Since the alloc has dynamic type, it is not converted into an alloca.
48 // CHECK-LABEL: func @condBranchDynamicType
49 func.func @condBranchDynamicType(
50   %arg0: i1,
51   %arg1: memref<?xf32>,
52   %arg2: memref<?xf32>,
53   %arg3: index) {
54   cf.cond_br %arg0, ^bb1, ^bb2(%arg3: index)
55 ^bb1:
56   cf.br ^bb3(%arg1 : memref<?xf32>)
57 ^bb2(%0: index):
58   %1 = memref.alloc(%0) : memref<?xf32>
59   test.buffer_based in(%arg1: memref<?xf32>) out(%1: memref<?xf32>)
60   cf.br ^bb3(%1 : memref<?xf32>)
61 ^bb3(%2: memref<?xf32>):
62   test.copy(%2, %arg2) : (memref<?xf32>, memref<?xf32>)
63   return
66 // CHECK-NEXT: cf.cond_br
67 //      CHECK: ^bb2
68 //      CHECK: ^bb2(%[[IDX:.*]]:{{.*}})
69 // CHECK-NEXT: %[[ALLOC0:.*]] = memref.alloc(%[[IDX]])
70 // CHECK-NEXT: test.buffer_based
71 //      CHECK: cf.br ^bb3
72 // CHECK-NEXT: ^bb3(%[[ALLOC0:.*]]:{{.*}})
73 //      CHECK: test.copy(%[[ALLOC0]],
74 // CHECK-NEXT: return
76 // -----
78 // CHECK-LABEL: func @dynamicRanked
79 func.func @dynamicRanked(%memref: memref<*xf32>) {
80   %0 = memref.rank %memref : memref<*xf32>
81   %1 = memref.alloc(%0) : memref<?xindex>
82   return
85 // CHECK-NEXT: %[[RANK:.*]] = memref.rank %{{.*}} : memref<*xf32>
86 // CHECK-NEXT: %[[ALLOCA:.*]] = memref.alloca(%[[RANK]])
88 // -----
90 // CHECK-LABEL: func @dynamicRanked2D
91 func.func @dynamicRanked2D(%memref: memref<*xf32>) {
92   %0 = memref.rank %memref : memref<*xf32>
93   %1 = memref.alloc(%0, %0) : memref<?x?xindex>
94   return
97 // CHECK-NEXT: %[[RANK:.*]] = memref.rank %{{.*}} : memref<*xf32>
98 //  RANK-NEXT: %[[ALLOC:.*]] = memref.alloca(%[[RANK]], %[[RANK]])
99 // DEFINDEX-NEXT: %[[ALLOC:.*]] = memref.alloc(%[[RANK]], %[[RANK]])
101 // -----
103 // CHECK-LABEL: func @dynamicNoRank
104 func.func @dynamicNoRank(%arg0: index) {
105   %0 = memref.alloc(%arg0) : memref<?xindex>
106   return
109 // CHECK-NEXT: %[[ALLOC:.*]] = memref.alloc
111 // -----
113 // Test Case: Existing AllocOp with no users.
114 // PromoteBuffersToStack expected behavior: It should convert it to an
115 // AllocaOp.
117 // CHECK-LABEL: func @emptyUsesValue
118 func.func @emptyUsesValue(%arg0: memref<4xf32>) {
119   %0 = memref.alloc() : memref<4xf32>
120   return
122 // CHECK-NEXT: %[[ALLOCA:.*]] = memref.alloca()
123 // CHECK-NEXT: return
125 // -----
127 // Test Case:
128 //    bb0
129 //   /   \
130 //  |    bb1 <- Initial position of AllocOp
131 //   \   /
132 //    bb2
133 // PromoteBuffersToStack expected behavior: It should convert it into an
134 // AllocaOp.
136 // CHECK-LABEL: func @criticalEdge
137 func.func @criticalEdge(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) {
138   cf.cond_br %arg0, ^bb1, ^bb2(%arg1 : memref<2xf32>)
139 ^bb1:
140   %0 = memref.alloc() : memref<2xf32>
141   test.buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>)
142   cf.br ^bb2(%0 : memref<2xf32>)
143 ^bb2(%1: memref<2xf32>):
144   test.copy(%1, %arg2) : (memref<2xf32>, memref<2xf32>)
145   return
148 // CHECK-NEXT: cf.cond_br {{.*}}
149 //      CHECK: ^bb1
150 // CHECK-NEXT: %[[ALLOCA:.*]] = memref.alloca()
151 //      CHECK: test.copy
152 // CHECK-NEXT: return
154 // -----
156 // Test Case:
157 //    bb0 <- Initial position of AllocOp
158 //   /   \
159 //  |    bb1
160 //   \   /
161 //    bb2
162 // PromoteBuffersToStack expected behavior: It converts the alloc in an alloca.
164 // CHECK-LABEL: func @invCriticalEdge
165 func.func @invCriticalEdge(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) {
166   %0 = memref.alloc() : memref<2xf32>
167   test.buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>)
168   cf.cond_br %arg0, ^bb1, ^bb2(%arg1 : memref<2xf32>)
169 ^bb1:
170   cf.br ^bb2(%0 : memref<2xf32>)
171 ^bb2(%1: memref<2xf32>):
172   test.copy(%1, %arg2) : (memref<2xf32>, memref<2xf32>)
173   return
176 // CHECK-NEXT: %[[ALLOCA:.*]] = memref.alloca()
177 //      CHECK: cf.cond_br
178 //      CHECK: test.copy
179 // CHECK-NEXT: return
181 // -----
183 // Test Case:
184 //    bb0 <- Initial position of the first AllocOp
185 //   /   \
186 //  bb1  bb2
187 //   \   /
188 //    bb3 <- Initial position of the second AllocOp
189 // PromoteBuffersToStack expected behavior: It converts the allocs into allocas.
191 // CHECK-LABEL: func @ifElse
192 func.func @ifElse(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) {
193   %0 = memref.alloc() : memref<2xf32>
194   test.buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>)
195   cf.cond_br %arg0,
196     ^bb1(%arg1, %0 : memref<2xf32>, memref<2xf32>),
197     ^bb2(%0, %arg1 : memref<2xf32>, memref<2xf32>)
198 ^bb1(%1: memref<2xf32>, %2: memref<2xf32>):
199   cf.br ^bb3(%1, %2 : memref<2xf32>, memref<2xf32>)
200 ^bb2(%3: memref<2xf32>, %4: memref<2xf32>):
201   cf.br ^bb3(%3, %4 : memref<2xf32>, memref<2xf32>)
202 ^bb3(%5: memref<2xf32>, %6: memref<2xf32>):
203   %7 = memref.alloc() : memref<2xf32>
204   test.buffer_based in(%5: memref<2xf32>) out(%7: memref<2xf32>)
205   test.copy(%7, %arg2) : (memref<2xf32>, memref<2xf32>)
206   return
209 // CHECK-NEXT: %[[ALLOCA0:.*]] = memref.alloca()
210 // CHECK-NEXT: test.buffer_based
211 //      CHECK: %[[ALLOCA1:.*]] = memref.alloca()
212 //      CHECK: test.buffer_based
213 //      CHECK: test.copy(%[[ALLOCA1]]
214 // CHECK-NEXT: return
216 // -----
218 // Test Case: No users for buffer in if-else CFG
219 //    bb0 <- Initial position of AllocOp
220 //   /   \
221 //  bb1  bb2
222 //   \   /
223 //    bb3
224 // PromoteBuffersToStack expected behavior: It converts the alloc into alloca.
226 // CHECK-LABEL: func @ifElseNoUsers
227 func.func @ifElseNoUsers(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) {
228   %0 = memref.alloc() : memref<2xf32>
229   test.buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>)
230   cf.cond_br %arg0,
231     ^bb1(%arg1, %0 : memref<2xf32>, memref<2xf32>),
232     ^bb2(%0, %arg1 : memref<2xf32>, memref<2xf32>)
233 ^bb1(%1: memref<2xf32>, %2: memref<2xf32>):
234   cf.br ^bb3(%1, %2 : memref<2xf32>, memref<2xf32>)
235 ^bb2(%3: memref<2xf32>, %4: memref<2xf32>):
236   cf.br ^bb3(%3, %4 : memref<2xf32>, memref<2xf32>)
237 ^bb3(%5: memref<2xf32>, %6: memref<2xf32>):
238   test.copy(%arg1, %arg2) : (memref<2xf32>, memref<2xf32>)
239   return
242 // CHECK-NEXT: %[[ALLOCA:.*]] = memref.alloca()
243 //      CHECK: return
245 // -----
247 // Test Case:
248 //      bb0 <- Initial position of the first AllocOp
249 //     /    \
250 //   bb1    bb2
251 //    |     /  \
252 //    |   bb3  bb4
253 //    \     \  /
254 //     \     /
255 //       bb5 <- Initial position of the second AllocOp
256 // PromoteBuffersToStack expected behavior: The two allocs should be converted
257 // into allocas.
259 // CHECK-LABEL: func @ifElseNested
260 func.func @ifElseNested(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) {
261   %0 = memref.alloc() : memref<2xf32>
262   test.buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>)
263   cf.cond_br %arg0,
264     ^bb1(%arg1, %0 : memref<2xf32>, memref<2xf32>),
265     ^bb2(%0, %arg1 : memref<2xf32>, memref<2xf32>)
266 ^bb1(%1: memref<2xf32>, %2: memref<2xf32>):
267   cf.br ^bb5(%1, %2 : memref<2xf32>, memref<2xf32>)
268 ^bb2(%3: memref<2xf32>, %4: memref<2xf32>):
269   cf.cond_br %arg0, ^bb3(%3 : memref<2xf32>), ^bb4(%4 : memref<2xf32>)
270 ^bb3(%5: memref<2xf32>):
271   cf.br ^bb5(%5, %3 : memref<2xf32>, memref<2xf32>)
272 ^bb4(%6: memref<2xf32>):
273   cf.br ^bb5(%3, %6 : memref<2xf32>, memref<2xf32>)
274 ^bb5(%7: memref<2xf32>, %8: memref<2xf32>):
275   %9 = memref.alloc() : memref<2xf32>
276   test.buffer_based in(%7: memref<2xf32>) out(%9: memref<2xf32>)
277   test.copy(%9, %arg2) : (memref<2xf32>, memref<2xf32>)
278   return
281 // CHECK-NEXT: %[[ALLOCA0:.*]] = memref.alloca()
282 // CHECK-NEXT: test.buffer_based
283 //      CHECK: %[[ALLOCA1:.*]] = memref.alloca()
284 //      CHECK: test.buffer_based
285 //      CHECK: test.copy(%[[ALLOCA1]]
286 // CHECK-NEXT: return
288 // -----
290 // Test Case: Dead operations in a single block.
291 // PromoteBuffersToStack expected behavior: It converts the two AllocOps into
292 // allocas.
294 // CHECK-LABEL: func @redundantOperations
295 func.func @redundantOperations(%arg0: memref<2xf32>) {
296   %0 = memref.alloc() : memref<2xf32>
297   test.buffer_based in(%arg0: memref<2xf32>) out(%0: memref<2xf32>)
298   %1 = memref.alloc() : memref<2xf32>
299   test.buffer_based in(%0: memref<2xf32>) out(%1: memref<2xf32>)
300   return
303 //      CHECK: (%[[ARG0:.*]]: {{.*}})
304 // CHECK-NEXT: %[[ALLOCA0:.*]] = memref.alloca()
305 // CHECK-NEXT: test.buffer_based in(%[[ARG0]]{{.*}} out(%[[ALLOCA0]]
306 //      CHECK: %[[ALLOCA1:.*]] = memref.alloca()
307 // CHECK-NEXT: test.buffer_based in(%[[ALLOCA0]]{{.*}} out(%[[ALLOCA1]]
308 //      CHECK: return
310 // -----
312 // Test Case:
313 //                                     bb0
314 //                                    /   \
315 // Initial pos of the 1st AllocOp -> bb1  bb2 <- Initial pos of the 2nd AllocOp
316 //                                    \   /
317 //                                     bb3
318 // PromoteBuffersToStack expected behavior: Both AllocOps are converted into
319 // allocas.
321 // CHECK-LABEL: func @moving_alloc_and_inserting_missing_dealloc
322 func.func @moving_alloc_and_inserting_missing_dealloc(
323   %cond: i1,
324     %arg0: memref<2xf32>,
325     %arg1: memref<2xf32>) {
326   cf.cond_br %cond, ^bb1, ^bb2
327 ^bb1:
328   %0 = memref.alloc() : memref<2xf32>
329   test.buffer_based in(%arg0: memref<2xf32>) out(%0: memref<2xf32>)
330   cf.br ^exit(%0 : memref<2xf32>)
331 ^bb2:
332   %1 = memref.alloc() : memref<2xf32>
333   test.buffer_based in(%arg0: memref<2xf32>) out(%1: memref<2xf32>)
334   cf.br ^exit(%1 : memref<2xf32>)
335 ^exit(%arg2: memref<2xf32>):
336   test.copy(%arg2, %arg1) : (memref<2xf32>, memref<2xf32>)
337   return
340 // CHECK-NEXT: cf.cond_br {{.*}}
341 //      CHECK: ^bb1
342 // CHECK-NEXT: %{{.*}} = memref.alloca()
343 //      CHECK: ^bb2
344 // CHECK-NEXT: %{{.*}} = memref.alloca()
345 //      CHECK: test.copy
346 // CHECK-NEXT: return
348 // -----
350 // Test Case: Nested regions - This test defines a BufferBasedOp inside the
351 // region of a RegionBufferBasedOp.
352 // PromoteBuffersToStack expected behavior: The AllocOps are converted into
353 // allocas.
355 // CHECK-LABEL: func @nested_regions_and_cond_branch
356 func.func @nested_regions_and_cond_branch(
357   %arg0: i1,
358   %arg1: memref<2xf32>,
359   %arg2: memref<2xf32>) {
360   cf.cond_br %arg0, ^bb1, ^bb2
361 ^bb1:
362   cf.br ^bb3(%arg1 : memref<2xf32>)
363 ^bb2:
364   %0 = memref.alloc() : memref<2xf32>
365   test.region_buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>) {
366   ^bb0(%gen1_arg0: f32, %gen1_arg1: f32):
367     %1 = memref.alloc() : memref<2xf32>
368     test.buffer_based in(%arg1: memref<2xf32>) out(%1: memref<2xf32>)
369     %tmp1 = math.exp %gen1_arg0 : f32
370     test.region_yield %tmp1 : f32
371   }
372   cf.br ^bb3(%0 : memref<2xf32>)
373 ^bb3(%1: memref<2xf32>):
374   test.copy(%1, %arg2) : (memref<2xf32>, memref<2xf32>)
375   return
378 // CHECK-NEXT:   cf.cond_br {{.*}}
379 //      CHECK:   ^bb2
380 // CHECK-NEXT:   %[[ALLOCA0:.*]] = memref.alloca()
381 //      CHECK:   ^bb0
382 // CHECK-NEXT:   %[[ALLOCA1:.*]] = memref.alloc()
384 // -----
386 // Test Case: buffer deallocation escaping
387 // PromoteBuffersToStack expected behavior: The first alloc is returned, so
388 // there is no conversion allowed. The second alloc is converted, since it
389 // only remains in the scope of the function.
391 // CHECK-LABEL: func @memref_in_function_results
392 func.func @memref_in_function_results(
393   %arg0: memref<5xf32>,
394   %arg1: memref<10xf32>,
395   %arg2: memref<5xf32>) -> (memref<10xf32>, memref<15xf32>) {
396   %x = memref.alloc() : memref<15xf32>
397   %y = memref.alloc() : memref<5xf32>
398   test.buffer_based in(%arg0: memref<5xf32>) out(%y: memref<5xf32>)
399   test.copy(%y, %arg2) : (memref<5xf32>, memref<5xf32>)
400   return %arg1, %x : memref<10xf32>, memref<15xf32>
402 //      CHECK: (%[[ARG0:.*]]: memref<5xf32>, %[[ARG1:.*]]: memref<10xf32>,
403 // CHECK-SAME: %[[RESULT:.*]]: memref<5xf32>)
404 //      CHECK: %[[ALLOC:.*]] = memref.alloc()
405 //      CHECK: %[[ALLOCA:.*]] = memref.alloca()
406 //      CHECK: test.copy
407 //      CHECK: return %[[ARG1]], %[[ALLOC]]
409 // -----
411 // Test Case: nested region control flow
412 // The allocation in the nested if branch cannot be converted to an alloca
413 // due to its dynamic memory allocation behavior.
415 // CHECK-LABEL: func @nested_region_control_flow
416 func.func @nested_region_control_flow(
417   %arg0 : index,
418   %arg1 : index) -> memref<?x?xf32> {
419   %0 = arith.cmpi eq, %arg0, %arg1 : index
420   %1 = memref.alloc(%arg0, %arg0) : memref<?x?xf32>
421   %2 = scf.if %0 -> (memref<?x?xf32>) {
422     scf.yield %1 : memref<?x?xf32>
423   } else {
424     %3 = memref.alloc(%arg0, %arg1) : memref<?x?xf32>
425     scf.yield %1 : memref<?x?xf32>
426   }
427   return %2 : memref<?x?xf32>
430 //      CHECK: %[[ALLOC0:.*]] = memref.alloc(%arg0, %arg0)
431 // CHECK-NEXT: %[[ALLOC1:.*]] = scf.if
432 //      CHECK: scf.yield %[[ALLOC0]]
433 //      CHECK: %[[ALLOC2:.*]] = memref.alloc(%arg0, %arg1)
434 // CHECK-NEXT: scf.yield %[[ALLOC0]]
435 //      CHECK: return %[[ALLOC1]]
437 // -----
439 // Test Case: nested region control flow within a region interface.
440 // The alloc %0 does not need to be converted in this case since the
441 // allocation finally escapes the method.
443 // CHECK-LABEL: func @inner_region_control_flow
444 func.func @inner_region_control_flow(%arg0 : index) -> memref<2x2xf32> {
445   %0 = memref.alloc() : memref<2x2xf32>
446   %1 = test.region_if %0 : memref<2x2xf32> -> (memref<2x2xf32>) then {
447     ^bb0(%arg1 : memref<2x2xf32>):
448       test.region_if_yield %arg1 : memref<2x2xf32>
449   } else {
450     ^bb0(%arg1 : memref<2x2xf32>):
451       test.region_if_yield %arg1 : memref<2x2xf32>
452   } join {
453     ^bb0(%arg1 : memref<2x2xf32>):
454       test.region_if_yield %arg1 : memref<2x2xf32>
455   }
456   return %1 : memref<2x2xf32>
459 //      CHECK: %[[ALLOC0:.*]] = memref.alloc()
460 // CHECK-NEXT: %[[ALLOC1:.*]] = test.region_if
461 // CHECK-NEXT: ^bb0(%[[ALLOC2:.*]]:{{.*}}):
462 // CHECK-NEXT: test.region_if_yield %[[ALLOC2]]
463 //      CHECK: ^bb0(%[[ALLOC3:.*]]:{{.*}}):
464 // CHECK-NEXT: test.region_if_yield %[[ALLOC3]]
465 //      CHECK: ^bb0(%[[ALLOC4:.*]]:{{.*}}):
466 // CHECK-NEXT: test.region_if_yield %[[ALLOC4]]
467 //      CHECK: return %[[ALLOC1]]
469 // -----
471 // Test Case: structured control-flow loop using a nested alloc.
472 // Alloc %0 will be converted to an alloca. %3 is not transformed.
474 // CHECK-LABEL: func @loop_alloc
475 func.func @loop_alloc(
476   %lb: index,
477   %ub: index,
478   %step: index,
479   %buf: memref<2xf32>,
480   %res: memref<2xf32>) {
481   %0 = memref.alloc() : memref<2xf32>
482   %1 = scf.for %i = %lb to %ub step %step
483     iter_args(%iterBuf = %buf) -> memref<2xf32> {
484     %2 = arith.cmpi eq, %i, %ub : index
485     %3 = memref.alloc() : memref<2xf32>
486     scf.yield %3 : memref<2xf32>
487   }
488   test.copy(%1, %res) : (memref<2xf32>, memref<2xf32>)
489   return
492 // CHECK-NEXT: %[[ALLOCA:.*]] = memref.alloca()
493 // CHECK-NEXT: scf.for
494 //      CHECK: %[[ALLOC:.*]] = memref.alloc()
496 // -----
498 // Test Case: structured control-flow loop with a nested if operation.
499 // The loop yields buffers that have been defined outside of the loop and the
500 // backedges only use the iteration arguments (or one of its aliases).
501 // Therefore, we do not have to (and are not allowed to) free any buffers
502 // that are passed via the backedges. The alloc is converted to an AllocaOp.
504 // CHECK-LABEL: func @loop_nested_if_no_alloc
505 func.func @loop_nested_if_no_alloc(
506   %lb: index,
507   %ub: index,
508   %step: index,
509   %buf: memref<2xf32>,
510   %res: memref<2xf32>) {
511   %0 = memref.alloc() : memref<2xf32>
512   %1 = scf.for %i = %lb to %ub step %step
513     iter_args(%iterBuf = %buf) -> memref<2xf32> {
514     %2 = arith.cmpi eq, %i, %ub : index
515     %3 = scf.if %2 -> (memref<2xf32>) {
516       scf.yield %0 : memref<2xf32>
517     } else {
518       scf.yield %iterBuf : memref<2xf32>
519     }
520     scf.yield %3 : memref<2xf32>
521   }
522   test.copy(%1, %res) : (memref<2xf32>, memref<2xf32>)
523   return
526 //      CHECK: %[[ALLOCA0:.*]] = memref.alloca()
527 // CHECK-NEXT: %[[ALLOCA1:.*]] = scf.for {{.*}} iter_args(%[[IALLOCA:.*]] =
528 //      CHECK: %[[ALLOCA2:.*]] = scf.if
529 //      CHECK: scf.yield %[[ALLOCA0]]
530 //      CHECK: scf.yield %[[IALLOCA]]
531 //      CHECK: scf.yield %[[ALLOCA2]]
532 //      CHECK: test.copy(%[[ALLOCA1]], %arg4)
534 // -----
536 // Test Case: structured control-flow loop with a nested if operation using
537 // a deeply nested buffer allocation.
538 // The allocs are not converted in this case.
540 // CHECK-LABEL: func @loop_nested_if_alloc
541 func.func @loop_nested_if_alloc(
542   %lb: index,
543   %ub: index,
544   %step: index,
545   %buf: memref<2xf32>) -> memref<2xf32> {
546   %0 = memref.alloc() : memref<2xf32>
547   %1 = scf.for %i = %lb to %ub step %step
548     iter_args(%iterBuf = %buf) -> memref<2xf32> {
549     %2 = arith.cmpi eq, %i, %ub : index
550     %3 = scf.if %2 -> (memref<2xf32>) {
551       %4 = memref.alloc() : memref<2xf32>
552       scf.yield %4 : memref<2xf32>
553     } else {
554       scf.yield %0 : memref<2xf32>
555     }
556     scf.yield %3 : memref<2xf32>
557   }
558   return %1 : memref<2xf32>
561 //      CHECK: %[[ALLOC0:.*]] = memref.alloc()
562 // CHECK-NEXT: %[[ALLOC1:.*]] = scf.for {{.*}}
563 //      CHECK: %[[ALLOC2:.*]] = scf.if
564 //      CHECK: %[[ALLOC3:.*]] = memref.alloc()
565 // CHECK-NEXT: scf.yield %[[ALLOC3]]
566 //      CHECK: scf.yield %[[ALLOC0]]
567 //      CHECK: scf.yield %[[ALLOC2]]
568 //      CHECK: return %[[ALLOC1]]
570 // -----
572 // Test Case: The allocated buffer is too large and, hence, it is not
573 // converted. In the actual implementation the largest size is 1KB.
575 // CHECK-LABEL: func @large_buffer_allocation
576 func.func @large_buffer_allocation(%arg0: memref<2048xf32>) {
577   %0 = memref.alloc() : memref<2048xf32>
578   test.copy(%0, %arg0) : (memref<2048xf32>, memref<2048xf32>)
579   return
582 // CHECK-NEXT: %[[ALLOC:.*]] = memref.alloc()
583 // CHECK-NEXT: test.copy
585 // -----
587 // Test Case: AllocOp with element type index.
588 // PromoteBuffersToStack expected behavior: It should convert it to an
589 // AllocaOp.
591 // CHECK-LABEL: func @indexElementType
592 func.func @indexElementType() {
593   %0 = memref.alloc() : memref<4xindex>
594   return
596 // DEFINDEX-NEXT: memref.alloca()
597 // LOWLIMIT-NEXT: memref.alloca()
598 // RANK-NEXT: memref.alloca()
599 // CHECK-NEXT: return
601 // -----
603 // CHECK-LABEL: func @bigIndexElementType
604 module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 256>>} {
605   func.func @bigIndexElementType() {
606     %0 = memref.alloc() {alignment = 64 : i64, custom_attr} : memref<4xindex>
607     return
608   }
610 // DEFINDEX-NEXT: memref.alloca() {alignment = 64 : i64, custom_attr}
611 // LOWLIMIT-NEXT: memref.alloc() {alignment = 64 : i64, custom_attr}
612 // RANK-NEXT: memref.alloca() {alignment = 64 : i64, custom_attr}
613 // CHECK-NEXT: return