1 // RUN: mlir-opt -buffer-deallocation -split-input-file %s | FileCheck %s
3 // This file checks the behaviour of BufferDeallocation pass for moving and
4 // inserting missing DeallocOps in their correct positions. Furthermore,
5 // copies and their corresponding AllocOps are inserted.
10 // bb1 bb2 <- Initial position of AllocOp
13 // BufferDeallocation expected behavior: bb2 contains an AllocOp which is
14 // passed to bb3. In the latter block, there should be an deallocation.
15 // Since bb1 does not contain an adequate alloc and the alloc in bb2 is not
16 // moved to bb0, we need to insert allocs and copies.
18 // CHECK-LABEL: func @condBranch
19 func @condBranch(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) {
20 cond_br %arg0, ^bb1, ^bb2
22 br ^bb3(%arg1 : memref<2xf32>)
24 %0 = alloc() : memref<2xf32>
25 test.buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>)
26 br ^bb3(%0 : memref<2xf32>)
27 ^bb3(%1: memref<2xf32>):
28 test.copy(%1, %arg2) : (memref<2xf32>, memref<2xf32>)
32 // CHECK-NEXT: cond_br
33 // CHECK: %[[ALLOC0:.*]] = alloc()
34 // CHECK-NEXT: linalg.copy
35 // CHECK-NEXT: br ^bb3(%[[ALLOC0]]
36 // CHECK: %[[ALLOC1:.*]] = alloc()
37 // CHECK-NEXT: test.buffer_based
38 // CHECK: %[[ALLOC2:.*]] = alloc()
39 // CHECK-NEXT: linalg.copy
40 // CHECK-NEXT: dealloc %[[ALLOC1]]
41 // CHECK-NEXT: br ^bb3(%[[ALLOC2]]
43 // CHECK-NEXT: dealloc
51 // bb1 bb2 <- Initial position of AllocOp
54 // BufferDeallocation expected behavior: The existing AllocOp has a dynamic
55 // dependency to block argument %0 in bb2. Since the dynamic type is passed
56 // to bb3 via the block argument %2, it is currently required to allocate a
57 // temporary buffer for %2 that gets copies of %arg0 and %1 with their
58 // appropriate shape dimensions. The copy buffer deallocation will be applied
59 // to %2 in block bb3.
61 // CHECK-LABEL: func @condBranchDynamicType
62 func @condBranchDynamicType(
67 cond_br %arg0, ^bb1, ^bb2(%arg3: index)
69 br ^bb3(%arg1 : memref<?xf32>)
71 %1 = alloc(%0) : memref<?xf32>
72 test.buffer_based in(%arg1: memref<?xf32>) out(%1: memref<?xf32>)
73 br ^bb3(%1 : memref<?xf32>)
74 ^bb3(%2: memref<?xf32>):
75 test.copy(%2, %arg2) : (memref<?xf32>, memref<?xf32>)
79 // CHECK-NEXT: cond_br
80 // CHECK: %[[DIM0:.*]] = dim
81 // CHECK-NEXT: %[[ALLOC0:.*]] = alloc(%[[DIM0]])
82 // CHECK-NEXT: linalg.copy(%{{.*}}, %[[ALLOC0]])
83 // CHECK-NEXT: br ^bb3(%[[ALLOC0]]
84 // CHECK: ^bb2(%[[IDX:.*]]:{{.*}})
85 // CHECK-NEXT: %[[ALLOC1:.*]] = alloc(%[[IDX]])
86 // CHECK-NEXT: test.buffer_based
87 // CHECK: %[[DIM1:.*]] = dim %[[ALLOC1]]
88 // CHECK-NEXT: %[[ALLOC2:.*]] = alloc(%[[DIM1]])
89 // CHECK-NEXT: linalg.copy(%[[ALLOC1]], %[[ALLOC2]])
90 // CHECK-NEXT: dealloc %[[ALLOC1]]
91 // CHECK-NEXT: br ^bb3
92 // CHECK-NEXT: ^bb3(%[[ALLOC3:.*]]:{{.*}})
93 // CHECK: test.copy(%[[ALLOC3]],
94 // CHECK-NEXT: dealloc %[[ALLOC3]]
102 // bb1 bb2 <- Initial position of AllocOp
111 // BufferDeallocation expected behavior: The existing AllocOp has a dynamic
112 // dependency to block argument %0 in bb2. Since the dynamic type is passed to
113 // bb5 via the block argument %2 and to bb6 via block argument %3, it is
114 // currently required to allocate temporary buffers for %2 and %3 that gets
115 // copies of %1 and %arg0 1 with their appropriate shape dimensions. The copy
116 // buffer deallocations will be applied to %2 in block bb5 and to %3 in block
117 // bb6. Furthermore, there should be no copy inserted for %4.
119 // CHECK-LABEL: func @condBranchDynamicTypeNested
120 func @condBranchDynamicTypeNested(
122 %arg1: memref<?xf32>,
123 %arg2: memref<?xf32>,
125 cond_br %arg0, ^bb1, ^bb2(%arg3: index)
127 br ^bb6(%arg1 : memref<?xf32>)
129 %1 = alloc(%0) : memref<?xf32>
130 test.buffer_based in(%arg1: memref<?xf32>) out(%1: memref<?xf32>)
131 cond_br %arg0, ^bb3, ^bb4
133 br ^bb5(%1 : memref<?xf32>)
135 br ^bb5(%1 : memref<?xf32>)
136 ^bb5(%2: memref<?xf32>):
137 br ^bb6(%2 : memref<?xf32>)
138 ^bb6(%3: memref<?xf32>):
139 br ^bb7(%3 : memref<?xf32>)
140 ^bb7(%4: memref<?xf32>):
141 test.copy(%4, %arg2) : (memref<?xf32>, memref<?xf32>)
145 // CHECK-NEXT: cond_br
147 // CHECK: %[[DIM0:.*]] = dim
148 // CHECK-NEXT: %[[ALLOC0:.*]] = alloc(%[[DIM0]])
149 // CHECK-NEXT: linalg.copy(%{{.*}}, %[[ALLOC0]])
150 // CHECK-NEXT: br ^bb6
151 // CHECK: ^bb2(%[[IDX:.*]]:{{.*}})
152 // CHECK-NEXT: %[[ALLOC1:.*]] = alloc(%[[IDX]])
153 // CHECK-NEXT: test.buffer_based
156 // CHECK-NEXT: br ^bb5(%[[ALLOC1]]{{.*}})
158 // CHECK-NEXT: br ^bb5(%[[ALLOC1]]{{.*}})
159 // CHECK-NEXT: ^bb5(%[[ALLOC2:.*]]:{{.*}})
160 // CHECK: %[[DIM2:.*]] = dim %[[ALLOC2]]
161 // CHECK-NEXT: %[[ALLOC3:.*]] = alloc(%[[DIM2]])
162 // CHECK-NEXT: linalg.copy(%[[ALLOC2]], %[[ALLOC3]])
163 // CHECK-NEXT: dealloc %[[ALLOC1]]
164 // CHECK-NEXT: br ^bb6(%[[ALLOC3]]{{.*}})
165 // CHECK-NEXT: ^bb6(%[[ALLOC4:.*]]:{{.*}})
166 // CHECK-NEXT: br ^bb7(%[[ALLOC4]]{{.*}})
167 // CHECK-NEXT: ^bb7(%[[ALLOC5:.*]]:{{.*}})
168 // CHECK: test.copy(%[[ALLOC5]],
169 // CHECK-NEXT: dealloc %[[ALLOC4]]
170 // CHECK-NEXT: return
174 // Test Case: Existing AllocOp with no users.
175 // BufferDeallocation expected behavior: It should insert a DeallocOp right
178 // CHECK-LABEL: func @emptyUsesValue
179 func @emptyUsesValue(%arg0: memref<4xf32>) {
180 %0 = alloc() : memref<4xf32>
183 // CHECK-NEXT: %[[ALLOC:.*]] = alloc()
184 // CHECK-NEXT: dealloc %[[ALLOC]]
185 // CHECK-NEXT: return
192 // | bb1 <- Initial position of AllocOp
195 // BufferDeallocation expected behavior: It should insert a DeallocOp at the
196 // exit block after CopyOp since %1 is an alias for %0 and %arg1. Furthermore,
197 // we have to insert a copy and an alloc in the beginning of the function.
199 // CHECK-LABEL: func @criticalEdge
200 func @criticalEdge(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) {
201 cond_br %arg0, ^bb1, ^bb2(%arg1 : memref<2xf32>)
203 %0 = alloc() : memref<2xf32>
204 test.buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>)
205 br ^bb2(%0 : memref<2xf32>)
206 ^bb2(%1: memref<2xf32>):
207 test.copy(%1, %arg2) : (memref<2xf32>, memref<2xf32>)
211 // CHECK-NEXT: %[[ALLOC0:.*]] = alloc()
212 // CHECK-NEXT: linalg.copy
213 // CHECK-NEXT: cond_br
214 // CHECK: %[[ALLOC1:.*]] = alloc()
215 // CHECK-NEXT: test.buffer_based
216 // CHECK: %[[ALLOC2:.*]] = alloc()
217 // CHECK-NEXT: linalg.copy
218 // CHECK-NEXT: dealloc %[[ALLOC1]]
220 // CHECK-NEXT: dealloc
221 // CHECK-NEXT: return
226 // bb0 <- Initial position of AllocOp
231 // BufferDeallocation expected behavior: It only inserts a DeallocOp at the
232 // exit block after CopyOp since %1 is an alias for %0 and %arg1.
234 // CHECK-LABEL: func @invCriticalEdge
235 func @invCriticalEdge(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) {
236 %0 = alloc() : memref<2xf32>
237 test.buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>)
238 cond_br %arg0, ^bb1, ^bb2(%arg1 : memref<2xf32>)
240 br ^bb2(%0 : memref<2xf32>)
241 ^bb2(%1: memref<2xf32>):
242 test.copy(%1, %arg2) : (memref<2xf32>, memref<2xf32>)
247 // CHECK-NEXT: return
252 // bb0 <- Initial position of the first AllocOp
256 // bb3 <- Initial position of the second AllocOp
257 // BufferDeallocation expected behavior: It only inserts two missing
258 // DeallocOps in the exit block. %5 is an alias for %0. Therefore, the
259 // DeallocOp for %0 should occur after the last BufferBasedOp. The Dealloc for
260 // %7 should happen after CopyOp.
262 // CHECK-LABEL: func @ifElse
263 func @ifElse(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) {
264 %0 = alloc() : memref<2xf32>
265 test.buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>)
267 ^bb1(%arg1, %0 : memref<2xf32>, memref<2xf32>),
268 ^bb2(%0, %arg1 : memref<2xf32>, memref<2xf32>)
269 ^bb1(%1: memref<2xf32>, %2: memref<2xf32>):
270 br ^bb3(%1, %2 : memref<2xf32>, memref<2xf32>)
271 ^bb2(%3: memref<2xf32>, %4: memref<2xf32>):
272 br ^bb3(%3, %4 : memref<2xf32>, memref<2xf32>)
273 ^bb3(%5: memref<2xf32>, %6: memref<2xf32>):
274 %7 = alloc() : memref<2xf32>
275 test.buffer_based in(%5: memref<2xf32>) out(%7: memref<2xf32>)
276 test.copy(%7, %arg2) : (memref<2xf32>, memref<2xf32>)
280 // CHECK-NEXT: %[[FIRST_ALLOC:.*]] = alloc()
281 // CHECK-NEXT: test.buffer_based
282 // CHECK: %[[SECOND_ALLOC:.*]] = alloc()
283 // CHECK-NEXT: test.buffer_based
284 // CHECK: dealloc %[[FIRST_ALLOC]]
286 // CHECK-NEXT: dealloc %[[SECOND_ALLOC]]
287 // CHECK-NEXT: return
291 // Test Case: No users for buffer in if-else CFG
292 // bb0 <- Initial position of AllocOp
297 // BufferDeallocation expected behavior: It only inserts a missing DeallocOp
298 // in the exit block since %5 or %6 are the latest aliases of %0.
300 // CHECK-LABEL: func @ifElseNoUsers
301 func @ifElseNoUsers(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) {
302 %0 = alloc() : memref<2xf32>
303 test.buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>)
305 ^bb1(%arg1, %0 : memref<2xf32>, memref<2xf32>),
306 ^bb2(%0, %arg1 : memref<2xf32>, memref<2xf32>)
307 ^bb1(%1: memref<2xf32>, %2: memref<2xf32>):
308 br ^bb3(%1, %2 : memref<2xf32>, memref<2xf32>)
309 ^bb2(%3: memref<2xf32>, %4: memref<2xf32>):
310 br ^bb3(%3, %4 : memref<2xf32>, memref<2xf32>)
311 ^bb3(%5: memref<2xf32>, %6: memref<2xf32>):
312 test.copy(%arg1, %arg2) : (memref<2xf32>, memref<2xf32>)
316 // CHECK-NEXT: %[[FIRST_ALLOC:.*]] = alloc()
318 // CHECK-NEXT: dealloc %[[FIRST_ALLOC]]
319 // CHECK-NEXT: return
324 // bb0 <- Initial position of the first AllocOp
331 // bb5 <- Initial position of the second AllocOp
332 // BufferDeallocation expected behavior: Two missing DeallocOps should be
333 // inserted in the exit block.
335 // CHECK-LABEL: func @ifElseNested
336 func @ifElseNested(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) {
337 %0 = alloc() : memref<2xf32>
338 test.buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>)
340 ^bb1(%arg1, %0 : memref<2xf32>, memref<2xf32>),
341 ^bb2(%0, %arg1 : memref<2xf32>, memref<2xf32>)
342 ^bb1(%1: memref<2xf32>, %2: memref<2xf32>):
343 br ^bb5(%1, %2 : memref<2xf32>, memref<2xf32>)
344 ^bb2(%3: memref<2xf32>, %4: memref<2xf32>):
345 cond_br %arg0, ^bb3(%3 : memref<2xf32>), ^bb4(%4 : memref<2xf32>)
346 ^bb3(%5: memref<2xf32>):
347 br ^bb5(%5, %3 : memref<2xf32>, memref<2xf32>)
348 ^bb4(%6: memref<2xf32>):
349 br ^bb5(%3, %6 : memref<2xf32>, memref<2xf32>)
350 ^bb5(%7: memref<2xf32>, %8: memref<2xf32>):
351 %9 = alloc() : memref<2xf32>
352 test.buffer_based in(%7: memref<2xf32>) out(%9: memref<2xf32>)
353 test.copy(%9, %arg2) : (memref<2xf32>, memref<2xf32>)
357 // CHECK-NEXT: %[[FIRST_ALLOC:.*]] = alloc()
358 // CHECK-NEXT: test.buffer_based
359 // CHECK: %[[SECOND_ALLOC:.*]] = alloc()
360 // CHECK-NEXT: test.buffer_based
361 // CHECK: dealloc %[[FIRST_ALLOC]]
363 // CHECK-NEXT: dealloc %[[SECOND_ALLOC]]
364 // CHECK-NEXT: return
368 // Test Case: Dead operations in a single block.
369 // BufferDeallocation expected behavior: It only inserts the two missing
370 // DeallocOps after the last BufferBasedOp.
372 // CHECK-LABEL: func @redundantOperations
373 func @redundantOperations(%arg0: memref<2xf32>) {
374 %0 = alloc() : memref<2xf32>
375 test.buffer_based in(%arg0: memref<2xf32>) out(%0: memref<2xf32>)
376 %1 = alloc() : memref<2xf32>
377 test.buffer_based in(%0: memref<2xf32>) out(%1: memref<2xf32>)
381 // CHECK: (%[[ARG0:.*]]: {{.*}})
382 // CHECK-NEXT: %[[FIRST_ALLOC:.*]] = alloc()
383 // CHECK-NEXT: test.buffer_based in(%[[ARG0]]{{.*}}out(%[[FIRST_ALLOC]]
384 // CHECK: %[[SECOND_ALLOC:.*]] = alloc()
385 // CHECK-NEXT: test.buffer_based in(%[[FIRST_ALLOC]]{{.*}}out(%[[SECOND_ALLOC]]
387 // CHECK-NEXT: dealloc
388 // CHECK-NEXT: return
395 // Initial pos of the 1st AllocOp -> bb1 bb2 <- Initial pos of the 2nd AllocOp
398 // BufferDeallocation expected behavior: We need to introduce a copy for each
399 // buffer since the buffers are passed to bb3. The both missing DeallocOps are
400 // inserted in the respective block of the allocs. The copy is freed in the exit
403 // CHECK-LABEL: func @moving_alloc_and_inserting_missing_dealloc
404 func @moving_alloc_and_inserting_missing_dealloc(
406 %arg0: memref<2xf32>,
407 %arg1: memref<2xf32>) {
408 cond_br %cond, ^bb1, ^bb2
410 %0 = alloc() : memref<2xf32>
411 test.buffer_based in(%arg0: memref<2xf32>) out(%0: memref<2xf32>)
412 br ^exit(%0 : memref<2xf32>)
414 %1 = alloc() : memref<2xf32>
415 test.buffer_based in(%arg0: memref<2xf32>) out(%1: memref<2xf32>)
416 br ^exit(%1 : memref<2xf32>)
417 ^exit(%arg2: memref<2xf32>):
418 test.copy(%arg2, %arg1) : (memref<2xf32>, memref<2xf32>)
422 // CHECK-NEXT: cond_br
425 // CHECK: %[[ALLOC0:.*]] = alloc()
426 // CHECK-NEXT: test.buffer_based
427 // CHECK: %[[ALLOC1:.*]] = alloc()
428 // CHECK-NEXT: linalg.copy
429 // CHECK-NEXT: dealloc %[[ALLOC0]]
430 // CHECK-NEXT: br ^bb3(%[[ALLOC1]]
432 // CHECK-NEXT: %[[ALLOC2:.*]] = alloc()
433 // CHECK-NEXT: test.buffer_based
434 // CHECK: %[[ALLOC3:.*]] = alloc()
435 // CHECK-NEXT: linalg.copy
436 // CHECK-NEXT: dealloc %[[ALLOC2]]
437 // CHECK-NEXT: br ^bb3(%[[ALLOC3]]
438 // CHECK-NEXT: ^bb3(%[[ALLOC4:.*]]:{{.*}})
440 // CHECK-NEXT: dealloc %[[ALLOC4]]
441 // CHECK-NEXT: return
445 // Test Case: Invalid position of the DeallocOp. There is a user after
449 // bb1 bb2 <- Initial position of AllocOp
452 // BufferDeallocation expected behavior: The existing DeallocOp should be
453 // moved to exit block.
455 // CHECK-LABEL: func @moving_invalid_dealloc_op_complex
456 func @moving_invalid_dealloc_op_complex(
458 %arg0: memref<2xf32>,
459 %arg1: memref<2xf32>) {
460 %1 = alloc() : memref<2xf32>
461 cond_br %cond, ^bb1, ^bb2
463 br ^exit(%arg0 : memref<2xf32>)
465 test.buffer_based in(%arg0: memref<2xf32>) out(%1: memref<2xf32>)
466 dealloc %1 : memref<2xf32>
467 br ^exit(%1 : memref<2xf32>)
468 ^exit(%arg2: memref<2xf32>):
469 test.copy(%arg2, %arg1) : (memref<2xf32>, memref<2xf32>)
473 // CHECK-NEXT: %[[ALLOC0:.*]] = alloc()
474 // CHECK-NEXT: cond_br
476 // CHECK-NEXT: dealloc %[[ALLOC0]]
477 // CHECK-NEXT: return
481 // Test Case: Inserting missing DeallocOp in a single block.
483 // CHECK-LABEL: func @inserting_missing_dealloc_simple
484 func @inserting_missing_dealloc_simple(
485 %arg0 : memref<2xf32>,
486 %arg1: memref<2xf32>) {
487 %0 = alloc() : memref<2xf32>
488 test.buffer_based in(%arg0: memref<2xf32>) out(%0: memref<2xf32>)
489 test.copy(%0, %arg1) : (memref<2xf32>, memref<2xf32>)
493 // CHECK-NEXT: %[[ALLOC0:.*]] = alloc()
495 // CHECK-NEXT: dealloc %[[ALLOC0]]
499 // Test Case: Moving invalid DeallocOp (there is a user after deallocation) in a
502 // CHECK-LABEL: func @moving_invalid_dealloc_op
503 func @moving_invalid_dealloc_op(%arg0 : memref<2xf32>, %arg1: memref<2xf32>) {
504 %0 = alloc() : memref<2xf32>
505 test.buffer_based in(%arg0: memref<2xf32>) out(%0: memref<2xf32>)
506 dealloc %0 : memref<2xf32>
507 test.copy(%0, %arg1) : (memref<2xf32>, memref<2xf32>)
511 // CHECK-NEXT: %[[ALLOC0:.*]] = alloc()
513 // CHECK-NEXT: dealloc %[[ALLOC0]]
517 // Test Case: Nested regions - This test defines a BufferBasedOp inside the
518 // region of a RegionBufferBasedOp.
519 // BufferDeallocation expected behavior: The AllocOp for the BufferBasedOp
520 // should remain inside the region of the RegionBufferBasedOp and it should insert
521 // the missing DeallocOp in the same region. The missing DeallocOp should be
522 // inserted after CopyOp.
524 // CHECK-LABEL: func @nested_regions_and_cond_branch
525 func @nested_regions_and_cond_branch(
527 %arg1: memref<2xf32>,
528 %arg2: memref<2xf32>) {
529 cond_br %arg0, ^bb1, ^bb2
531 br ^bb3(%arg1 : memref<2xf32>)
533 %0 = alloc() : memref<2xf32>
534 test.region_buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>) {
535 ^bb0(%gen1_arg0: f32, %gen1_arg1: f32):
536 %1 = alloc() : memref<2xf32>
537 test.buffer_based in(%arg1: memref<2xf32>) out(%1: memref<2xf32>)
538 %tmp1 = exp %gen1_arg0 : f32
539 test.region_yield %tmp1 : f32
541 br ^bb3(%0 : memref<2xf32>)
542 ^bb3(%1: memref<2xf32>):
543 test.copy(%1, %arg2) : (memref<2xf32>, memref<2xf32>)
546 // CHECK: (%[[cond:.*]]: {{.*}}, %[[ARG1:.*]]: {{.*}}, %{{.*}}: {{.*}})
547 // CHECK-NEXT: cond_br %[[cond]], ^[[BB1:.*]], ^[[BB2:.*]]
548 // CHECK: %[[ALLOC0:.*]] = alloc()
549 // CHECK-NEXT: linalg.copy(%[[ARG1]], %[[ALLOC0]])
551 // CHECK: %[[ALLOC1:.*]] = alloc()
552 // CHECK-NEXT: test.region_buffer_based in(%[[ARG1]]{{.*}}out(%[[ALLOC1]]
553 // CHECK: %[[ALLOC2:.*]] = alloc()
554 // CHECK-NEXT: test.buffer_based in(%[[ARG1]]{{.*}}out(%[[ALLOC2]]
555 // CHECK: dealloc %[[ALLOC2]]
556 // CHECK-NEXT: %{{.*}} = exp
557 // CHECK: %[[ALLOC3:.*]] = alloc()
558 // CHECK-NEXT: linalg.copy(%[[ALLOC1]], %[[ALLOC3]])
559 // CHECK-NEXT: dealloc %[[ALLOC1]]
560 // CHECK: ^[[BB3:.*]]({{.*}}):
562 // CHECK-NEXT: dealloc
566 // Test Case: buffer deallocation escaping
567 // BufferDeallocation expected behavior: It must not dealloc %arg1 and %x
568 // since they are operands of return operation and should escape from
569 // deallocating. It should dealloc %y after CopyOp.
571 // CHECK-LABEL: func @memref_in_function_results
572 func @memref_in_function_results(
573 %arg0: memref<5xf32>,
574 %arg1: memref<10xf32>,
575 %arg2: memref<5xf32>) -> (memref<10xf32>, memref<15xf32>) {
576 %x = alloc() : memref<15xf32>
577 %y = alloc() : memref<5xf32>
578 test.buffer_based in(%arg0: memref<5xf32>) out(%y: memref<5xf32>)
579 test.copy(%y, %arg2) : (memref<5xf32>, memref<5xf32>)
580 return %arg1, %x : memref<10xf32>, memref<15xf32>
582 // CHECK: (%[[ARG0:.*]]: memref<5xf32>, %[[ARG1:.*]]: memref<10xf32>,
583 // CHECK-SAME: %[[RESULT:.*]]: memref<5xf32>)
584 // CHECK: %[[X:.*]] = alloc()
585 // CHECK: %[[Y:.*]] = alloc()
587 // CHECK: dealloc %[[Y]]
588 // CHECK: return %[[ARG1]], %[[X]]
592 // Test Case: nested region control flow
593 // The alloc %1 flows through both if branches until it is finally returned.
594 // Hence, it does not require a specific dealloc operation. However, %3
595 // requires a dealloc.
597 // CHECK-LABEL: func @nested_region_control_flow
598 func @nested_region_control_flow(
600 %arg1 : index) -> memref<?x?xf32> {
601 %0 = cmpi "eq", %arg0, %arg1 : index
602 %1 = alloc(%arg0, %arg0) : memref<?x?xf32>
603 %2 = scf.if %0 -> (memref<?x?xf32>) {
604 scf.yield %1 : memref<?x?xf32>
606 %3 = alloc(%arg0, %arg1) : memref<?x?xf32>
607 scf.yield %1 : memref<?x?xf32>
609 return %2 : memref<?x?xf32>
612 // CHECK: %[[ALLOC0:.*]] = alloc(%arg0, %arg0)
613 // CHECK-NEXT: %[[ALLOC1:.*]] = scf.if
614 // CHECK: scf.yield %[[ALLOC0]]
615 // CHECK: %[[ALLOC2:.*]] = alloc(%arg0, %arg1)
616 // CHECK-NEXT: dealloc %[[ALLOC2]]
617 // CHECK-NEXT: scf.yield %[[ALLOC0]]
618 // CHECK: return %[[ALLOC1]]
622 // Test Case: nested region control flow with a nested buffer allocation in a
624 // Buffer deallocation places a copy for both %1 and %3, since they are
625 // returned in the end.
627 // CHECK-LABEL: func @nested_region_control_flow_div
628 func @nested_region_control_flow_div(
630 %arg1 : index) -> memref<?x?xf32> {
631 %0 = cmpi "eq", %arg0, %arg1 : index
632 %1 = alloc(%arg0, %arg0) : memref<?x?xf32>
633 %2 = scf.if %0 -> (memref<?x?xf32>) {
634 scf.yield %1 : memref<?x?xf32>
636 %3 = alloc(%arg0, %arg1) : memref<?x?xf32>
637 scf.yield %3 : memref<?x?xf32>
639 return %2 : memref<?x?xf32>
642 // CHECK: %[[ALLOC0:.*]] = alloc(%arg0, %arg0)
643 // CHECK-NEXT: %[[ALLOC1:.*]] = scf.if
644 // CHECK: %[[ALLOC2:.*]] = alloc
645 // CHECK-NEXT: linalg.copy(%[[ALLOC0]], %[[ALLOC2]])
646 // CHECK: scf.yield %[[ALLOC2]]
647 // CHECK: %[[ALLOC3:.*]] = alloc(%arg0, %arg1)
648 // CHECK: %[[ALLOC4:.*]] = alloc
649 // CHECK-NEXT: linalg.copy(%[[ALLOC3]], %[[ALLOC4]])
650 // CHECK: dealloc %[[ALLOC3]]
651 // CHECK: scf.yield %[[ALLOC4]]
652 // CHECK: dealloc %[[ALLOC0]]
653 // CHECK-NEXT: return %[[ALLOC1]]
657 // Test Case: nested region control flow within a region interface.
658 // No copies are required in this case since the allocation finally escapes
661 // CHECK-LABEL: func @inner_region_control_flow
662 func @inner_region_control_flow(%arg0 : index) -> memref<?x?xf32> {
663 %0 = alloc(%arg0, %arg0) : memref<?x?xf32>
664 %1 = test.region_if %0 : memref<?x?xf32> -> (memref<?x?xf32>) then {
665 ^bb0(%arg1 : memref<?x?xf32>):
666 test.region_if_yield %arg1 : memref<?x?xf32>
668 ^bb0(%arg1 : memref<?x?xf32>):
669 test.region_if_yield %arg1 : memref<?x?xf32>
671 ^bb0(%arg1 : memref<?x?xf32>):
672 test.region_if_yield %arg1 : memref<?x?xf32>
674 return %1 : memref<?x?xf32>
677 // CHECK: %[[ALLOC0:.*]] = alloc(%arg0, %arg0)
678 // CHECK-NEXT: %[[ALLOC1:.*]] = test.region_if
679 // CHECK-NEXT: ^bb0(%[[ALLOC2:.*]]:{{.*}}):
680 // CHECK-NEXT: test.region_if_yield %[[ALLOC2]]
681 // CHECK: ^bb0(%[[ALLOC3:.*]]:{{.*}}):
682 // CHECK-NEXT: test.region_if_yield %[[ALLOC3]]
683 // CHECK: ^bb0(%[[ALLOC4:.*]]:{{.*}}):
684 // CHECK-NEXT: test.region_if_yield %[[ALLOC4]]
685 // CHECK: return %[[ALLOC1]]
689 // CHECK-LABEL: func @subview
690 func @subview(%arg0 : index, %arg1 : index, %arg2 : memref<?x?xf32>) {
691 %0 = alloc() : memref<64x4xf32, offset: 0, strides: [4, 1]>
692 %1 = subview %0[%arg0, %arg1][%arg0, %arg1][%arg0, %arg1] :
693 memref<64x4xf32, offset: 0, strides: [4, 1]>
694 to memref<?x?xf32, offset: ?, strides: [?, ?]>
695 test.copy(%1, %arg2) :
696 (memref<?x?xf32, offset: ?, strides: [?, ?]>, memref<?x?xf32>)
700 // CHECK-NEXT: %[[ALLOC:.*]] = alloc()
701 // CHECK-NEXT: subview
702 // CHECK-NEXT: test.copy
703 // CHECK-NEXT: dealloc %[[ALLOC]]
704 // CHECK-NEXT: return
708 // Test Case: In the presence of AllocaOps only the AllocOps has top be freed.
709 // Therefore, all allocas are not handled.
711 // CHECK-LABEL: func @condBranchAlloca
712 func @condBranchAlloca(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) {
713 cond_br %arg0, ^bb1, ^bb2
715 br ^bb3(%arg1 : memref<2xf32>)
717 %0 = alloca() : memref<2xf32>
718 test.buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>)
719 br ^bb3(%0 : memref<2xf32>)
720 ^bb3(%1: memref<2xf32>):
721 test.copy(%1, %arg2) : (memref<2xf32>, memref<2xf32>)
725 // CHECK-NEXT: cond_br
726 // CHECK: %[[ALLOCA:.*]] = alloca()
727 // CHECK: br ^bb3(%[[ALLOCA:.*]])
729 // CHECK-NEXT: test.copy
730 // CHECK-NEXT: return
734 // Test Case: In the presence of AllocaOps only the AllocOps has top be freed.
735 // Therefore, all allocas are not handled. In this case, only alloc %0 has a
738 // CHECK-LABEL: func @ifElseAlloca
739 func @ifElseAlloca(%arg0: i1, %arg1: memref<2xf32>, %arg2: memref<2xf32>) {
740 %0 = alloc() : memref<2xf32>
741 test.buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>)
743 ^bb1(%arg1, %0 : memref<2xf32>, memref<2xf32>),
744 ^bb2(%0, %arg1 : memref<2xf32>, memref<2xf32>)
745 ^bb1(%1: memref<2xf32>, %2: memref<2xf32>):
746 br ^bb3(%1, %2 : memref<2xf32>, memref<2xf32>)
747 ^bb2(%3: memref<2xf32>, %4: memref<2xf32>):
748 br ^bb3(%3, %4 : memref<2xf32>, memref<2xf32>)
749 ^bb3(%5: memref<2xf32>, %6: memref<2xf32>):
750 %7 = alloca() : memref<2xf32>
751 test.buffer_based in(%5: memref<2xf32>) out(%7: memref<2xf32>)
752 test.copy(%7, %arg2) : (memref<2xf32>, memref<2xf32>)
756 // CHECK-NEXT: %[[ALLOC:.*]] = alloc()
757 // CHECK-NEXT: test.buffer_based
758 // CHECK: %[[ALLOCA:.*]] = alloca()
759 // CHECK-NEXT: test.buffer_based
760 // CHECK: dealloc %[[ALLOC]]
762 // CHECK-NEXT: return
766 // CHECK-LABEL: func @ifElseNestedAlloca
767 func @ifElseNestedAlloca(
769 %arg1: memref<2xf32>,
770 %arg2: memref<2xf32>) {
771 %0 = alloca() : memref<2xf32>
772 test.buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>)
774 ^bb1(%arg1, %0 : memref<2xf32>, memref<2xf32>),
775 ^bb2(%0, %arg1 : memref<2xf32>, memref<2xf32>)
776 ^bb1(%1: memref<2xf32>, %2: memref<2xf32>):
777 br ^bb5(%1, %2 : memref<2xf32>, memref<2xf32>)
778 ^bb2(%3: memref<2xf32>, %4: memref<2xf32>):
779 cond_br %arg0, ^bb3(%3 : memref<2xf32>), ^bb4(%4 : memref<2xf32>)
780 ^bb3(%5: memref<2xf32>):
781 br ^bb5(%5, %3 : memref<2xf32>, memref<2xf32>)
782 ^bb4(%6: memref<2xf32>):
783 br ^bb5(%3, %6 : memref<2xf32>, memref<2xf32>)
784 ^bb5(%7: memref<2xf32>, %8: memref<2xf32>):
785 %9 = alloc() : memref<2xf32>
786 test.buffer_based in(%7: memref<2xf32>) out(%9: memref<2xf32>)
787 test.copy(%9, %arg2) : (memref<2xf32>, memref<2xf32>)
791 // CHECK-NEXT: %[[ALLOCA:.*]] = alloca()
792 // CHECK-NEXT: test.buffer_based
793 // CHECK: %[[ALLOC:.*]] = alloc()
794 // CHECK-NEXT: test.buffer_based
796 // CHECK-NEXT: dealloc %[[ALLOC]]
797 // CHECK-NEXT: return
801 // CHECK-LABEL: func @nestedRegionsAndCondBranchAlloca
802 func @nestedRegionsAndCondBranchAlloca(
804 %arg1: memref<2xf32>,
805 %arg2: memref<2xf32>) {
806 cond_br %arg0, ^bb1, ^bb2
808 br ^bb3(%arg1 : memref<2xf32>)
810 %0 = alloc() : memref<2xf32>
811 test.region_buffer_based in(%arg1: memref<2xf32>) out(%0: memref<2xf32>) {
812 ^bb0(%gen1_arg0: f32, %gen1_arg1: f32):
813 %1 = alloca() : memref<2xf32>
814 test.buffer_based in(%arg1: memref<2xf32>) out(%1: memref<2xf32>)
815 %tmp1 = exp %gen1_arg0 : f32
816 test.region_yield %tmp1 : f32
818 br ^bb3(%0 : memref<2xf32>)
819 ^bb3(%1: memref<2xf32>):
820 test.copy(%1, %arg2) : (memref<2xf32>, memref<2xf32>)
823 // CHECK: (%[[cond:.*]]: {{.*}}, %[[ARG1:.*]]: {{.*}}, %{{.*}}: {{.*}})
824 // CHECK-NEXT: cond_br %[[cond]], ^[[BB1:.*]], ^[[BB2:.*]]
826 // CHECK: %[[ALLOC0:.*]] = alloc()
827 // CHECK-NEXT: linalg.copy
829 // CHECK: %[[ALLOC1:.*]] = alloc()
830 // CHECK-NEXT: test.region_buffer_based in(%[[ARG1]]{{.*}}out(%[[ALLOC1]]
831 // CHECK: %[[ALLOCA:.*]] = alloca()
832 // CHECK-NEXT: test.buffer_based in(%[[ARG1]]{{.*}}out(%[[ALLOCA]]
833 // CHECK: %{{.*}} = exp
834 // CHECK: %[[ALLOC2:.*]] = alloc()
835 // CHECK-NEXT: linalg.copy
836 // CHECK-NEXT: dealloc %[[ALLOC1]]
837 // CHECK: ^[[BB3:.*]]({{.*}}):
839 // CHECK-NEXT: dealloc
843 // CHECK-LABEL: func @nestedRegionControlFlowAlloca
844 func @nestedRegionControlFlowAlloca(
846 %arg1 : index) -> memref<?x?xf32> {
847 %0 = cmpi "eq", %arg0, %arg1 : index
848 %1 = alloc(%arg0, %arg0) : memref<?x?xf32>
849 %2 = scf.if %0 -> (memref<?x?xf32>) {
850 scf.yield %1 : memref<?x?xf32>
852 %3 = alloca(%arg0, %arg1) : memref<?x?xf32>
853 scf.yield %1 : memref<?x?xf32>
855 return %2 : memref<?x?xf32>
858 // CHECK: %[[ALLOC0:.*]] = alloc(%arg0, %arg0)
859 // CHECK-NEXT: %[[ALLOC1:.*]] = scf.if
860 // CHECK: scf.yield %[[ALLOC0]]
861 // CHECK: %[[ALLOCA:.*]] = alloca(%arg0, %arg1)
862 // CHECK-NEXT: scf.yield %[[ALLOC0]]
863 // CHECK: return %[[ALLOC1]]
867 // Test Case: structured control-flow loop using a nested alloc.
868 // The iteration argument %iterBuf has to be freed before yielding %3 to avoid
871 // CHECK-LABEL: func @loop_alloc
877 %res: memref<2xf32>) {
878 %0 = alloc() : memref<2xf32>
879 %1 = scf.for %i = %lb to %ub step %step
880 iter_args(%iterBuf = %buf) -> memref<2xf32> {
881 %2 = cmpi "eq", %i, %ub : index
882 %3 = alloc() : memref<2xf32>
883 scf.yield %3 : memref<2xf32>
885 test.copy(%1, %res) : (memref<2xf32>, memref<2xf32>)
889 // CHECK: %[[ALLOC0:.*]] = alloc()
890 // CHECK-NEXT: dealloc %[[ALLOC0]]
891 // CHECK-NEXT: %[[ALLOC1:.*]] = alloc()
892 // CHECK: linalg.copy(%arg3, %[[ALLOC1]])
893 // CHECK: %[[ALLOC2:.*]] = scf.for {{.*}} iter_args
894 // CHECK-SAME: (%[[IALLOC:.*]] = %[[ALLOC1]]
896 // CHECK: dealloc %[[IALLOC]]
897 // CHECK: %[[ALLOC3:.*]] = alloc()
898 // CHECK: %[[ALLOC4:.*]] = alloc()
899 // CHECK: linalg.copy(%[[ALLOC3]], %[[ALLOC4]])
900 // CHECK: dealloc %[[ALLOC3]]
901 // CHECK: scf.yield %[[ALLOC4]]
903 // CHECK: test.copy(%[[ALLOC2]], %arg4)
904 // CHECK-NEXT: dealloc %[[ALLOC2]]
908 // Test Case: structured control-flow loop with a nested if operation.
909 // The loop yields buffers that have been defined outside of the loop and the
910 // backeges only use the iteration arguments (or one of its aliases).
911 // Therefore, we do not have to (and are not allowed to) free any buffers
912 // that are passed via the backedges.
914 // CHECK-LABEL: func @loop_nested_if_no_alloc
915 func @loop_nested_if_no_alloc(
920 %res: memref<2xf32>) {
921 %0 = alloc() : memref<2xf32>
922 %1 = scf.for %i = %lb to %ub step %step
923 iter_args(%iterBuf = %buf) -> memref<2xf32> {
924 %2 = cmpi "eq", %i, %ub : index
925 %3 = scf.if %2 -> (memref<2xf32>) {
926 scf.yield %0 : memref<2xf32>
928 scf.yield %iterBuf : memref<2xf32>
930 scf.yield %3 : memref<2xf32>
932 test.copy(%1, %res) : (memref<2xf32>, memref<2xf32>)
936 // CHECK: %[[ALLOC0:.*]] = alloc()
937 // CHECK-NEXT: %[[ALLOC1:.*]] = scf.for {{.*}} iter_args(%[[IALLOC:.*]] =
938 // CHECK: %[[ALLOC2:.*]] = scf.if
939 // CHECK: scf.yield %[[ALLOC0]]
940 // CHECK: scf.yield %[[IALLOC]]
941 // CHECK: scf.yield %[[ALLOC2]]
942 // CHECK: test.copy(%[[ALLOC1]], %arg4)
943 // CHECK: dealloc %[[ALLOC0]]
947 // Test Case: structured control-flow loop with a nested if operation using
948 // a deeply nested buffer allocation.
949 // Since the innermost allocation happens in a divergent branch, we have to
950 // introduce additional copies for the nested if operation. Since the loop's
951 // yield operation "returns" %3, it will return a newly allocated buffer.
952 // Therefore, we have to free the iteration argument %iterBuf before
955 // CHECK-LABEL: func @loop_nested_if_alloc
956 func @loop_nested_if_alloc(
960 %buf: memref<2xf32>) -> memref<2xf32> {
961 %0 = alloc() : memref<2xf32>
962 %1 = scf.for %i = %lb to %ub step %step
963 iter_args(%iterBuf = %buf) -> memref<2xf32> {
964 %2 = cmpi "eq", %i, %ub : index
965 %3 = scf.if %2 -> (memref<2xf32>) {
966 %4 = alloc() : memref<2xf32>
967 scf.yield %4 : memref<2xf32>
969 scf.yield %0 : memref<2xf32>
971 scf.yield %3 : memref<2xf32>
973 return %1 : memref<2xf32>
976 // CHECK: %[[ALLOC0:.*]] = alloc()
977 // CHECK: %[[ALLOC1:.*]] = alloc()
978 // CHECK-NEXT: linalg.copy(%arg3, %[[ALLOC1]])
979 // CHECK-NEXT: %[[ALLOC2:.*]] = scf.for {{.*}} iter_args
980 // CHECK-SAME: (%[[IALLOC:.*]] = %[[ALLOC1]]
981 // CHECK: dealloc %[[IALLOC]]
982 // CHECK: %[[ALLOC3:.*]] = scf.if
984 // CHECK: %[[ALLOC4:.*]] = alloc()
985 // CHECK-NEXT: %[[ALLOC5:.*]] = alloc()
986 // CHECK-NEXT: linalg.copy(%[[ALLOC4]], %[[ALLOC5]])
987 // CHECK-NEXT: dealloc %[[ALLOC4]]
988 // CHECK-NEXT: scf.yield %[[ALLOC5]]
990 // CHECK: %[[ALLOC6:.*]] = alloc()
991 // CHECK-NEXT: linalg.copy(%[[ALLOC0]], %[[ALLOC6]])
992 // CHECK-NEXT: scf.yield %[[ALLOC6]]
994 // CHECK: %[[ALLOC7:.*]] = alloc()
995 // CHECK-NEXT: linalg.copy(%[[ALLOC3:.*]], %[[ALLOC7]])
996 // CHECK-NEXT: dealloc %[[ALLOC3]]
997 // CHECK-NEXT: scf.yield %[[ALLOC7]]
999 // CHECK: dealloc %[[ALLOC0]]
1000 // CHECK-NEXT: return %[[ALLOC2]]
1004 // Test Case: several nested structured control-flow loops with a deeply nested
1005 // buffer allocation inside an if operation.
1006 // Same behavior is an loop_nested_if_alloc: we have to insert deallocations
1007 // before each yield in all loops recursively.
1009 // CHECK-LABEL: func @loop_nested_alloc
1010 func @loop_nested_alloc(
1014 %buf: memref<2xf32>,
1015 %res: memref<2xf32>) {
1016 %0 = alloc() : memref<2xf32>
1017 %1 = scf.for %i = %lb to %ub step %step
1018 iter_args(%iterBuf = %buf) -> memref<2xf32> {
1019 %2 = scf.for %i2 = %lb to %ub step %step
1020 iter_args(%iterBuf2 = %iterBuf) -> memref<2xf32> {
1021 %3 = scf.for %i3 = %lb to %ub step %step
1022 iter_args(%iterBuf3 = %iterBuf2) -> memref<2xf32> {
1023 %4 = alloc() : memref<2xf32>
1024 %5 = cmpi "eq", %i, %ub : index
1025 %6 = scf.if %5 -> (memref<2xf32>) {
1026 %7 = alloc() : memref<2xf32>
1027 scf.yield %7 : memref<2xf32>
1029 scf.yield %iterBuf3 : memref<2xf32>
1031 scf.yield %6 : memref<2xf32>
1033 scf.yield %3 : memref<2xf32>
1035 scf.yield %2 : memref<2xf32>
1037 test.copy(%1, %res) : (memref<2xf32>, memref<2xf32>)
1041 // CHECK: %[[ALLOC0:.*]] = alloc()
1042 // CHECK-NEXT: dealloc %[[ALLOC0]]
1043 // CHECK-NEXT: %[[ALLOC1:.*]] = alloc()
1044 // CHECK-NEXT: linalg.copy(%arg3, %[[ALLOC1]])
1045 // CHECK-NEXT: %[[VAL_7:.*]] = scf.for {{.*}} iter_args
1046 // CHECK-SAME: (%[[IALLOC0:.*]] = %[[ALLOC1]])
1047 // CHECK: %[[ALLOC2:.*]] = alloc()
1048 // CHECK-NEXT: linalg.copy(%[[IALLOC0]], %[[ALLOC2]])
1049 // CHECK-NEXT: dealloc %[[IALLOC0]]
1050 // CHECK-NEXT: %[[ALLOC3:.*]] = scf.for {{.*}} iter_args
1051 // CHECK-SAME: (%[[IALLOC1:.*]] = %[[ALLOC2]])
1052 // CHECK: %[[ALLOC5:.*]] = alloc()
1053 // CHECK-NEXT: linalg.copy(%[[IALLOC1]], %[[ALLOC5]])
1054 // CHECK-NEXT: dealloc %[[IALLOC1]]
1056 // CHECK: %[[ALLOC6:.*]] = scf.for {{.*}} iter_args
1057 // CHECK-SAME: (%[[IALLOC2:.*]] = %[[ALLOC5]])
1058 // CHECK: %[[ALLOC8:.*]] = alloc()
1059 // CHECK-NEXT: dealloc %[[ALLOC8]]
1060 // CHECK: %[[ALLOC9:.*]] = scf.if
1062 // CHECK: %[[ALLOC11:.*]] = alloc()
1063 // CHECK-NEXT: %[[ALLOC12:.*]] = alloc()
1064 // CHECK-NEXT: linalg.copy(%[[ALLOC11]], %[[ALLOC12]])
1065 // CHECK-NEXT: dealloc %[[ALLOC11]]
1066 // CHECK-NEXT: scf.yield %[[ALLOC12]]
1068 // CHECK: %[[ALLOC13:.*]] = alloc()
1069 // CHECK-NEXT: linalg.copy(%[[IALLOC2]], %[[ALLOC13]])
1070 // CHECK-NEXT: scf.yield %[[ALLOC13]]
1072 // CHECK: dealloc %[[IALLOC2]]
1073 // CHECK-NEXT: %[[ALLOC10:.*]] = alloc()
1074 // CHECK-NEXT: linalg.copy(%[[ALLOC9]], %[[ALLOC10]])
1075 // CHECK-NEXT: dealloc %[[ALLOC9]]
1076 // CHECK-NEXT: scf.yield %[[ALLOC10]]
1078 // CHECK: %[[ALLOC7:.*]] = alloc()
1079 // CHECK-NEXT: linalg.copy(%[[ALLOC6]], %[[ALLOC7]])
1080 // CHECK-NEXT: dealloc %[[ALLOC6]]
1081 // CHECK-NEXT: scf.yield %[[ALLOC7]]
1083 // CHECK: %[[ALLOC4:.*]] = alloc()
1084 // CHECK-NEXT: linalg.copy(%[[ALLOC3]], %[[ALLOC4]])
1085 // CHECK-NEXT: dealloc %[[ALLOC3]]
1086 // CHECK-NEXT: scf.yield %[[ALLOC4]]
1088 // CHECK: test.copy(%[[VAL_7]], %arg4)
1089 // CHECK-NEXT: dealloc %[[VAL_7]]
1093 // Test Case: explicit control-flow loop with a dynamically allocated buffer.
1094 // The BufferDeallocation transformation should fail on this explicit
1095 // control-flow loop since they are not supported.
1097 // CHECK-LABEL: func @loop_dynalloc
1098 func @loop_dynalloc(
1101 %arg2: memref<?xf32>,
1102 %arg3: memref<?xf32>) {
1103 %const0 = constant 0 : i32
1104 br ^loopHeader(%const0, %arg2 : i32, memref<?xf32>)
1106 ^loopHeader(%i : i32, %buff : memref<?xf32>):
1107 %lessThan = cmpi "slt", %i, %arg1 : i32
1109 ^loopBody(%i, %buff : i32, memref<?xf32>),
1110 ^exit(%buff : memref<?xf32>)
1112 ^loopBody(%val : i32, %buff2: memref<?xf32>):
1113 %const1 = constant 1 : i32
1114 %inc = addi %val, %const1 : i32
1115 %size = std.index_cast %inc : i32 to index
1116 %alloc1 = alloc(%size) : memref<?xf32>
1117 br ^loopHeader(%inc, %alloc1 : i32, memref<?xf32>)
1119 ^exit(%buff3 : memref<?xf32>):
1120 test.copy(%buff3, %arg3) : (memref<?xf32>, memref<?xf32>)
1124 // expected-error@+1 {{Structured control-flow loops are supported only}}
1128 // Test Case: explicit control-flow loop with a dynamically allocated buffer.
1129 // The BufferDeallocation transformation should fail on this explicit
1130 // control-flow loop since they are not supported.
1132 // CHECK-LABEL: func @do_loop_alloc
1133 func @do_loop_alloc(
1136 %arg2: memref<2xf32>,
1137 %arg3: memref<2xf32>) {
1138 %const0 = constant 0 : i32
1139 br ^loopBody(%const0, %arg2 : i32, memref<2xf32>)
1141 ^loopBody(%val : i32, %buff2: memref<2xf32>):
1142 %const1 = constant 1 : i32
1143 %inc = addi %val, %const1 : i32
1144 %alloc1 = alloc() : memref<2xf32>
1145 br ^loopHeader(%inc, %alloc1 : i32, memref<2xf32>)
1147 ^loopHeader(%i : i32, %buff : memref<2xf32>):
1148 %lessThan = cmpi "slt", %i, %arg1 : i32
1150 ^loopBody(%i, %buff : i32, memref<2xf32>),
1151 ^exit(%buff : memref<2xf32>)
1153 ^exit(%buff3 : memref<2xf32>):
1154 test.copy(%buff3, %arg3) : (memref<2xf32>, memref<2xf32>)
1158 // expected-error@+1 {{Structured control-flow loops are supported only}}
1162 // CHECK-LABEL: func @assumingOp(
1164 %arg0: !shape.witness,
1165 %arg2: memref<2xf32>,
1166 %arg3: memref<2xf32>) {
1167 // Confirm the alloc will be dealloc'ed in the block.
1168 %1 = shape.assuming %arg0 -> memref<2xf32> {
1169 %0 = alloc() : memref<2xf32>
1170 shape.assuming_yield %arg2 : memref<2xf32>
1172 // Confirm the alloc will be returned and dealloc'ed after its use.
1173 %3 = shape.assuming %arg0 -> memref<2xf32> {
1174 %2 = alloc() : memref<2xf32>
1175 shape.assuming_yield %2 : memref<2xf32>
1177 test.copy(%3, %arg3) : (memref<2xf32>, memref<2xf32>)
1181 // CHECK-SAME: %[[ARG0:.*]]: !shape.witness,
1182 // CHECK-SAME: %[[ARG1:.*]]: {{.*}},
1183 // CHECK-SAME: %[[ARG2:.*]]: {{.*}}
1184 // CHECK: %[[UNUSED_RESULT:.*]] = shape.assuming %[[ARG0]]
1185 // CHECK-NEXT: %[[ALLOC0:.*]] = alloc()
1186 // CHECK-NEXT: dealloc %[[ALLOC0]]
1187 // CHECK-NEXT: shape.assuming_yield %[[ARG1]]
1188 // CHECK: %[[ASSUMING_RESULT:.*]] = shape.assuming %[[ARG0]]
1189 // CHECK-NEXT: %[[TMP_ALLOC:.*]] = alloc()
1190 // CHECK-NEXT: %[[RETURNING_ALLOC:.*]] = alloc()
1191 // CHECK-NEXT: linalg.copy(%[[TMP_ALLOC]], %[[RETURNING_ALLOC]])
1192 // CHECK-NEXT: dealloc %[[TMP_ALLOC]]
1193 // CHECK-NEXT: shape.assuming_yield %[[RETURNING_ALLOC]]
1194 // CHECK: test.copy(%[[ASSUMING_RESULT:.*]], %[[ARG2]])
1195 // CHECK-NEXT: dealloc %[[ASSUMING_RESULT]]