1 // RUN: mlir-opt -split-input-file -test-liveness-analysis %s 2>&1 | FileCheck %s
3 // Positive test: Type (1.a) "is an operand of an op with memory effects"
4 // zero is live because it is stored in memory.
5 // CHECK-LABEL: test_tag: zero:
6 // CHECK-NEXT: result #0: live
7 func.func @test_1_type_1.a(%arg0: memref<i32>) {
8 %c0_i32 = arith.constant {tag = "zero"} 0 : i32
9 memref.store %c0_i32, %arg0[] : memref<i32>
15 // Positive test: Type (1.b) "is a non-forwarded branch operand and a block
16 // where its op could take the control has an op with memory effects"
17 // %arg2 is live because it can make the control go into a block with a memory
19 // CHECK-LABEL: test_tag: br:
20 // CHECK-NEXT: operand #0: live
21 // CHECK-NEXT: operand #1: live
22 // CHECK-NEXT: operand #2: live
23 func.func @test_2_RegionBranchOpInterface_type_1.b(%arg0: memref<i32>, %arg1: memref<i32>, %arg2: i1) {
24 %c0_i32 = arith.constant 0 : i32
25 cf.cond_br %arg2, ^bb1(%c0_i32 : i32), ^bb2(%c0_i32 : i32) {tag = "br"}
27 memref.store %0, %arg0[] : memref<i32>
30 memref.store %1, %arg1[] : memref<i32>
38 // Positive test: Type (1.b) "is a non-forwarded branch operand and a block
39 // where its op could take the control has an op with memory effects"
40 // %arg0 is live because it can make the control go into a block with a memory
42 // CHECK-LABEL: test_tag: flag:
43 // CHECK-NEXT: operand #0: live
44 func.func @test_3_BranchOpInterface_type_1.b(%arg0: i32, %arg1: memref<i32>, %arg2: memref<i32>) {
45 %c0_i32 = arith.constant 0 : i32
46 cf.switch %arg0 : i32, [
51 memref.store %c0_i32, %arg1[] : memref<i32>
54 memref.store %c0_i32, %arg2[] : memref<i32>
62 func.func private @private(%arg0 : i32, %arg1 : i32) {
66 // Positive test: Type (1.c) "is a non-forwarded call operand"
67 // CHECK-LABEL: test_tag: call
68 // CHECK-LABEL: operand #0: not live
69 // CHECK-LABEL: operand #1: not live
70 // CHECK-LABEL: operand #2: live
71 func.func @test_4_type_1.c(%arg0: i32, %arg1: i32, %device: i32, %m0: memref<i32>) {
72 test.call_on_device @private(%arg0, %arg1), %device {tag = "call"} : (i32, i32, i32) -> ()
78 // Positive test: Type (2) "is returned by a public function"
79 // zero is live because it is returned by a public function.
80 // CHECK-LABEL: test_tag: zero:
81 // CHECK-NEXT: result #0: live
82 func.func @test_5_type_2() -> (f32){
83 %0 = arith.constant {tag = "zero"} 0.0 : f32
89 // Positive test: Type (3) "is used to compute a value of type (1) or (2)"
90 // %arg1 is live because the scf.while has a live result and %arg1 is a
91 // non-forwarded branch operand.
92 // %arg2 is live because it is forwarded to the live result of the scf.while
94 // %arg5 is live because it is forwarded to %arg8 which is live.
95 // %arg8 is live because it is forwarded to %arg4 which is live as it writes
98 // %arg3 is not live even though %arg1, %arg2, and %arg5 are live because it
99 // is neither a non-forwarded branch operand nor a forwarded operand that
100 // forwards to a live value. It actually is a forwarded operand that forwards
101 // to non-live values %0#1 and %arg7.
102 // CHECK-LABEL: test_tag: condition:
103 // CHECK-NEXT: operand #0: live
104 // CHECK-NEXT: operand #1: live
105 // CHECK-NEXT: operand #2: not live
106 // CHECK-NEXT: operand #3: live
107 // CHECK-LABEL: test_tag: add:
108 // CHECK-NEXT: operand #0: live
109 func.func @test_6_RegionBranchTerminatorOpInterface_type_3(%arg0: memref<i32>, %arg1: i1) -> (i32) {
110 %c0_i32 = arith.constant 0 : i32
111 %c1_i32 = arith.constant 1 : i32
112 %c2_i32 = arith.constant 2 : i32
113 %0:3 = scf.while (%arg2 = %c0_i32, %arg3 = %c1_i32, %arg4 = %c2_i32, %arg5 = %c2_i32) : (i32, i32, i32, i32) -> (i32, i32, i32) {
114 memref.store %arg4, %arg0[] : memref<i32>
115 scf.condition(%arg1) {tag = "condition"} %arg2, %arg3, %arg5 : i32, i32, i32
117 ^bb0(%arg6: i32, %arg7: i32, %arg8: i32):
118 %1 = arith.addi %arg8, %arg8 {tag = "add"} : i32
119 %c3_i32 = arith.constant 3 : i32
120 scf.yield %arg6, %arg7, %arg8, %c3_i32 : i32, i32, i32, i32
127 func.func private @private0(%0 : i32) -> i32 {
128 %1 = arith.addi %0, %0 {tag = "in_private0"} : i32
132 // Positive test: Type (3) "is used to compute a value of type (1) or (2)"
133 // zero, ten, and one are live because they are used to decide the number of
134 // times the `for` loop executes, which in turn decides the value stored in
136 // in_private0 and x are also live because they decide the value stored in
139 // y is not live even though the non-forwarded branch operand and x are live.
140 // CHECK-LABEL: test_tag: in_private0:
141 // CHECK-NEXT: operand #0: live
142 // CHECK-NEXT: operand #1: live
143 // CHECK-NEXT: result #0: live
144 // CHECK-LABEL: test_tag: zero:
145 // CHECK-NEXT: result #0: live
146 // CHECK-LABEL: test_tag: ten:
147 // CHECK-NEXT: result #0: live
148 // CHECK-LABEL: test_tag: one:
149 // CHECK-NEXT: result #0: live
150 // CHECK-LABEL: test_tag: x:
151 // CHECK-NEXT: result #0: live
152 // CHECK-LABEL: test_tag: y:
153 // CHECK-NEXT: result #0: not live
154 func.func @test_7_type_3(%arg0: memref<i32>) {
155 %c0 = arith.constant {tag = "zero"} 0 : index
156 %c10 = arith.constant {tag = "ten"} 10 : index
157 %c1 = arith.constant {tag = "one"} 1 : index
158 %x = arith.constant {tag = "x"} 0 : i32
159 %y = arith.constant {tag = "y"} 1 : i32
160 %0:2 = scf.for %arg1 = %c0 to %c10 step %c1 iter_args(%arg2 = %x, %arg3 = %y) -> (i32, i32) {
161 %1 = arith.addi %x, %x : i32
162 %2 = func.call @private0(%1) : (i32) -> i32
163 scf.yield %2, %arg3 : i32, i32
165 memref.store %0#0, %arg0[] : memref<i32>
171 func.func private @private1(%0 : i32) -> i32 {
172 %1 = func.call @private2(%0) : (i32) -> i32
173 %2 = arith.muli %0, %1 {tag = "in_private1"} : i32
177 func.func private @private2(%0 : i32) -> i32 {
178 %cond = arith.index_cast %0 {tag = "in_private2"} : i32 to index
179 %1 = scf.index_switch %cond -> i32
181 %ten = arith.constant 10 : i32
185 %twenty = arith.constant 20 : i32
186 scf.yield %twenty : i32
189 %thirty = arith.constant 30 : i32
190 scf.yield %thirty : i32
195 // Positive test: Type (3) "is used to compute a value of type (1) or (2)"
196 // in_private1, in_private2, and final are live because they are used to compute
197 // the value returned by this public function.
198 // CHECK-LABEL: test_tag: in_private1:
199 // CHECK-NEXT: operand #0: live
200 // CHECK-NEXT: operand #1: live
201 // CHECK-NEXT: result #0: live
202 // CHECK-LABEL: test_tag: in_private2:
203 // CHECK-NEXT: operand #0: live
204 // CHECK-NEXT: result #0: live
205 // CHECK-LABEL: test_tag: final:
206 // CHECK-NEXT: operand #0: live
207 // CHECK-NEXT: operand #1: live
208 // CHECK-NEXT: result #0: live
209 func.func @test_8_type_3(%arg: i32) -> (i32) {
210 %0 = func.call @private1(%arg) : (i32) -> i32
211 %final = arith.muli %0, %arg {tag = "final"} : i32
217 // Negative test: None of the types (1), (2), or (3)
218 // zero is not live because it has no effect outside the program: it doesn't
219 // affect the memory or the program output.
220 // CHECK-LABEL: test_tag: zero:
221 // CHECK-NEXT: result #0: not live
222 // CHECK-LABEL: test_tag: one:
223 // CHECK-NEXT: result #0: live
224 func.func @test_9_negative() -> (f32){
225 %0 = arith.constant {tag = "zero"} 0.0 : f32
226 %1 = arith.constant {tag = "one"} 1.0 : f32
232 // Negative test: None of the types (1), (2), or (3)
233 // %1 is not live because it has no effect outside the program: it doesn't
234 // affect the memory or the program output. Even though it is returned by the
235 // function `@private_1`, it is never used by the caller.
236 // Note that this test clearly shows how this liveness analysis utility differs
237 // from the existing liveness utility present at
238 // llvm-project/mlir/include/mlir/Analysis/Liveness.h. The latter marks %1 as
239 // live as it exists the block of function `@private_1`, simply because it is
240 // computed inside and returned by the block, irrespective of whether or not it
241 // is used by the caller.
242 // CHECK-LABEL: test_tag: one:
243 // CHECK: result #0: not live
244 func.func private @private_1() -> (i32, i32) {
245 %0 = arith.constant 0 : i32
246 %1 = arith.addi %0, %0 {tag = "one"} : i32
247 return %0, %1 : i32, i32
249 func.func @test_10_negative() -> (i32) {
250 %0:2 = func.call @private_1() : () -> (i32, i32)