xref: /llvm-project/mlir/test/Analysis/DataFlow/test-written-to.mlir (revision 32a4e3fccaf304c8d541bdefdb1a7ef829f84c1c)
1// RUN: mlir-opt -split-input-file -test-written-to %s 2>&1 |\
2// RUN:          FileCheck %s --check-prefixes=CHECK,IP
3// RUN: mlir-opt -split-input-file -test-written-to='interprocedural=false' %s \
4// RUN:          2>&1 | FileCheck %s --check-prefixes=CHECK,LOCAL
5// RUN: mlir-opt -split-input-file \
6// RUN:          -test-written-to='assume-func-writes=true' %s 2>&1 |\
7// RUN:          FileCheck %s --check-prefixes=CHECK,IP_AW
8// RUN: mlir-opt -split-input-file \
9// RUN:       -test-written-to='interprocedural=false assume-func-writes=true' \
10// RUN:       %s 2>&1 | FileCheck %s --check-prefixes=CHECK,LC_AW
11
12// Check prefixes are as follows:
13// 'check': common for all runs;
14// 'ip': interprocedural runs;
15// 'ip_aw': interpocedural runs assuming calls to external functions write to
16//          all arguments;
17// 'local': local (non-interprocedural) analysis not assuming calls writing;
18// 'lc_aw': local analysis assuming external calls writing to all arguments.
19
20// Note that despite the name of the test analysis being "written to", it is set
21// up in a peculiar way where passing a value through a block or region argument
22// (via visitCall/BranchOperand) is considered as "writing" that value to the
23// corresponding operand, which is itself a value and not necessarily "memory".
24// This is arguably okay for testing purposes, but may be surprising for readers
25// trying to interpret this test using their intuition.
26
27// CHECK-LABEL: test_tag: constant0
28// CHECK: result #0: [a]
29// CHECK-LABEL: test_tag: constant1
30// CHECK: result #0: [b]
31func.func @test_two_writes(%m0: memref<i32>, %m1: memref<i32>) -> (memref<i32>, memref<i32>) {
32  %c0 = arith.constant {tag = "constant0"} 0 : i32
33  %c1 = arith.constant {tag = "constant1"} 1 : i32
34  memref.store %c0, %m0[] {tag_name = "a"} : memref<i32>
35  memref.store %c1, %m1[] {tag_name = "b"} : memref<i32>
36  return %m0, %m1 : memref<i32>, memref<i32>
37}
38
39// -----
40
41// CHECK-LABEL: test_tag: c0
42// CHECK: result #0: [b]
43// CHECK-LABEL: test_tag: c1
44// CHECK: result #0: [b]
45// CHECK-LABEL: test_tag: condition
46// CHECK: result #0: [brancharg0]
47// CHECK-LABEL: test_tag: c2
48// CHECK: result #0: [a]
49// CHECK-LABEL: test_tag: c3
50// CHECK: result #0: [a]
51func.func @test_if(%m0: memref<i32>, %m1: memref<i32>, %condition: i1) {
52  %c0 = arith.constant {tag = "c0"} 2 : i32
53  %c1 = arith.constant {tag = "c1"} 3 : i32
54  %condition2 = arith.addi %condition, %condition {tag = "condition"} : i1
55  %0, %1 = scf.if %condition2 -> (i32, i32) {
56    %c2 = arith.constant {tag = "c2"} 0 : i32
57    scf.yield %c2, %c0: i32, i32
58  } else {
59    %c3 = arith.constant {tag = "c3"} 1 : i32
60    scf.yield %c3, %c1: i32, i32
61  }
62  memref.store %0, %m0[] {tag_name = "a"} : memref<i32>
63  memref.store %1, %m1[] {tag_name = "b"} : memref<i32>
64  return
65}
66
67// -----
68
69// CHECK-LABEL: test_tag: c0
70// CHECK: result #0: [a c]
71// CHECK-LABEL: test_tag: c1
72// CHECK: result #0: [b c]
73// CHECK-LABEL: test_tag: br
74// CHECK: operand #0: [brancharg0]
75func.func @test_blocks(%m0: memref<i32>,
76                       %m1: memref<i32>,
77                       %m2: memref<i32>, %cond : i1) {
78  %0 = arith.constant {tag = "c0"} 0 : i32
79  %1 = arith.constant {tag = "c1"} 1 : i32
80  cf.cond_br %cond, ^a(%0: i32), ^b(%1: i32) {tag = "br"}
81^a(%a0: i32):
82  memref.store %a0, %m0[] {tag_name = "a"} : memref<i32>
83  cf.br ^c(%a0 : i32)
84^b(%b0: i32):
85  memref.store %b0, %m1[] {tag_name = "b"} : memref<i32>
86  cf.br ^c(%b0 : i32)
87^c(%c0 : i32):
88  memref.store %c0, %m2[] {tag_name = "c"} : memref<i32>
89  return
90}
91
92// -----
93
94// CHECK-LABEL: test_tag: two
95// CHECK: result #0: [a]
96func.func @test_infinite_loop(%m0: memref<i32>) {
97  %0 = arith.constant 0 : i32
98  %1 = arith.constant 1 : i32
99  %2 = arith.constant {tag = "two"} 2 : i32
100  %3 = arith.constant -1 : i32
101  cf.br ^loop(%0, %1, %2: i32, i32, i32)
102^loop(%a: i32, %b: i32, %c: i32):
103  memref.store %a, %m0[] {tag_name = "a"} : memref<i32>
104  cf.br ^loop(%b, %c, %3 : i32, i32, i32)
105}
106
107// -----
108
109// CHECK-LABEL: test_tag: c0
110// CHECK: result #0: [a b c]
111func.func @test_switch(%flag: i32, %m0: memref<i32>) {
112  %0 = arith.constant {tag = "c0"} 0 : i32
113  cf.switch %flag : i32, [
114      default: ^a(%0 : i32),
115      42: ^b(%0 : i32),
116      43: ^c(%0 : i32)
117  ]
118^a(%a0: i32):
119  memref.store %a0, %m0[] {tag_name = "a"} : memref<i32>
120  cf.br ^c(%a0 : i32)
121^b(%b0: i32):
122  memref.store %b0, %m0[] {tag_name = "b"} : memref<i32>
123  cf.br ^c(%b0 : i32)
124^c(%c0 : i32):
125  memref.store %c0, %m0[] {tag_name = "c"} : memref<i32>
126  return
127}
128
129// -----
130
131// CHECK-LABEL: test_tag: add
132// IP:    result #0: [a]
133// LOCAL: result #0: [callarg0]
134// LC_AW: result #0: [func.call]
135func.func @test_caller(%m0: memref<f32>, %arg: f32) {
136  %0 = arith.addf %arg, %arg {tag = "add"} : f32
137  %1 = func.call @callee(%0) : (f32) -> f32
138  %2 = arith.mulf %1, %1 : f32
139  %3 = arith.mulf %2, %2 : f32
140  %4 = arith.mulf %3, %3 : f32
141  memref.store %4, %m0[] {tag_name = "a"} : memref<f32>
142  return
143}
144
145func.func private @callee(%0 : f32) -> f32 {
146  %1 = arith.mulf %0, %0 : f32
147  %2 = arith.mulf %1, %1 : f32
148  func.return %2 : f32
149}
150
151// -----
152
153func.func private @callee(%0 : f32) -> f32 {
154  %1 = arith.mulf %0, %0 : f32
155  func.return %1 : f32
156}
157
158// CHECK-LABEL: test_tag: sub
159// IP:    result #0: [a]
160// LOCAL: result #0: [callarg0]
161// LC_AW: result #0: [func.call]
162func.func @test_caller_below_callee(%m0: memref<f32>, %arg: f32) {
163  %0 = arith.subf %arg, %arg {tag = "sub"} : f32
164  %1 = func.call @callee(%0) : (f32) -> f32
165  memref.store %1, %m0[] {tag_name = "a"} : memref<f32>
166  return
167}
168
169// -----
170
171func.func private @callee1(%0 : f32) -> f32 {
172  %1 = func.call @callee2(%0) : (f32) -> f32
173  func.return %1 : f32
174}
175
176func.func private @callee2(%0 : f32) -> f32 {
177  %1 = func.call @callee3(%0) : (f32) -> f32
178  func.return %1 : f32
179}
180
181func.func private @callee3(%0 : f32) -> f32 {
182  func.return %0 : f32
183}
184
185// CHECK-LABEL: test_tag: mul
186// IP:    result #0: [a]
187// LOCAL: result #0: [callarg0]
188// LC_AW: result #0: [func.call]
189func.func @test_callchain(%m0: memref<f32>, %arg: f32) {
190  %0 = arith.mulf %arg, %arg {tag = "mul"} : f32
191  %1 = func.call @callee1(%0) : (f32) -> f32
192  memref.store %1, %m0[] {tag_name = "a"} : memref<f32>
193  return
194}
195
196// -----
197
198// CHECK-LABEL: test_tag: zero
199// CHECK: result #0: [c]
200// CHECK-LABEL: test_tag: init
201// CHECK: result #0: [a b c]
202// CHECK-LABEL: test_tag: condition
203// CHECK: operand #0: [brancharg0]
204// CHECK: operand #2: [a b c]
205func.func @test_while(%m0: memref<i32>, %init : i32, %cond: i1) {
206  %zero = arith.constant {tag = "zero"} 0 : i32
207  %init2 = arith.addi %init, %init {tag = "init"} : i32
208  %0, %1 = scf.while (%arg1 = %zero, %arg2 = %init2) : (i32, i32) -> (i32, i32) {
209    memref.store %arg2, %m0[] {tag_name = "a"} : memref<i32>
210    scf.condition(%cond) {tag = "condition"} %arg1, %arg2 : i32, i32
211  } do {
212   ^bb0(%arg1: i32, %arg2: i32):
213    memref.store %arg1, %m0[] {tag_name = "c"} : memref<i32>
214    %res = arith.addi %arg2, %arg2 : i32
215    scf.yield %res, %res: i32, i32
216  }
217  memref.store %1, %m0[] {tag_name = "b"} : memref<i32>
218  return
219}
220
221// -----
222
223// CHECK-LABEL: test_tag: zero
224// CHECK: result #0: []
225// CHECK-LABEL: test_tag: one
226// CHECK: result #0: [a]
227// CHECK-LABEL: test_tag: condition
228// CHECK: operand #0: [brancharg0]
229//
230// The important thing to note in this test is that the sparse backward dataflow
231// analysis framework also works on complex region branch ops like this one
232// where the number of operands in the `scf.yield` op don't match the number of
233// results in the parent op.
234func.func @test_complex_while(%m0: memref<i32>, %cond: i1) {
235  %zero = arith.constant {tag = "zero"} 0 : i32
236  %one = arith.constant {tag = "one"} 1 : i32
237  %0 = scf.while (%arg1 = %zero, %arg2 = %one) : (i32, i32) -> (i32) {
238    scf.condition(%cond) {tag = "condition"} %arg2 : i32
239  } do {
240   ^bb0(%arg1: i32):
241    scf.yield %arg1, %arg1: i32, i32
242  }
243  memref.store %0, %m0[] {tag_name = "a"} : memref<i32>
244  return
245}
246
247// -----
248
249// CHECK-LABEL: test_tag: zero
250// CHECK: result #0: [brancharg0]
251// CHECK-LABEL: test_tag: ten
252// CHECK: result #0: [brancharg1]
253// CHECK-LABEL: test_tag: one
254// CHECK: result #0: [brancharg2]
255// CHECK-LABEL: test_tag: x
256// CHECK: result #0: [a]
257func.func @test_for(%m0: memref<i32>) {
258  %zero = arith.constant {tag = "zero"} 0 : index
259  %ten = arith.constant {tag = "ten"} 10 : index
260  %one = arith.constant {tag = "one"} 1 : index
261  %x = arith.constant {tag = "x"} 0 : i32
262  %0 = scf.for %i = %zero to %ten step %one iter_args(%ix = %x) -> (i32) {
263    scf.yield %ix : i32
264  }
265  memref.store %0, %m0[] {tag_name = "a"} : memref<i32>
266  return
267}
268
269// -----
270
271// CHECK-LABEL: test_tag: default_a
272// CHECK:       result #0: [a]
273// CHECK-LABEL: test_tag: default_b
274// CHECK:       result #0: [b]
275// CHECK-LABEL: test_tag: 1a
276// CHECK:       result #0: [a]
277// CHECK-LABEL: test_tag: 1b
278// CHECK:       result #0: [b]
279// CHECK-LABEL: test_tag: 2a
280// CHECK:       result #0: [a]
281// CHECK-LABEL: test_tag: 2b
282// CHECK:       result #0: [b]
283// CHECK-LABEL: test_tag: switch
284// CHECK:       operand #0: [brancharg0]
285func.func @test_switch(%arg0 : index, %m0: memref<i32>) {
286  %0, %1 = scf.index_switch %arg0 {tag="switch"} -> i32, i32
287  case 1 {
288    %2 = arith.constant {tag="1a"} 10 : i32
289    %3 = arith.constant {tag="1b"} 100 : i32
290    scf.yield %2, %3 : i32, i32
291  }
292  case 2 {
293    %4 = arith.constant {tag="2a"} 20 : i32
294    %5 = arith.constant {tag="2b"} 200 : i32
295    scf.yield %4, %5 : i32, i32
296  }
297  default {
298    %6 = arith.constant {tag="default_a"} 30 : i32
299    %7 = arith.constant {tag="default_b"} 300 : i32
300    scf.yield %6, %7 : i32, i32
301  }
302  memref.store %0, %m0[] {tag_name = "a"} : memref<i32>
303  memref.store %1, %m0[] {tag_name = "b"} : memref<i32>
304  return
305}
306
307// -----
308
309// The point of this test is to ensure the analysis doesn't crash in presence of
310// external functions.
311
312// CHECK-LABEL: llvm.func @decl(i64)
313// CHECK-LABEL: llvm.func @func(%arg0: i64) {
314// CHECK-NEXT:  llvm.call @decl(%arg0) : (i64) -> ()
315// CHECK-NEXT:  llvm.return
316
317llvm.func @decl(i64)
318
319llvm.func @func(%lb : i64) -> () {
320  llvm.call @decl(%lb) : (i64) -> ()
321  llvm.return
322}
323
324// -----
325
326func.func private @callee(%arg0 : i32, %arg1 : i32) -> i32 {
327  func.return %arg0 : i32
328}
329
330// CHECK-LABEL: test_tag: a
331
332// IP:           operand #0: [b]
333// LOCAL:        operand #0: [callarg0]
334// LC_AW:        operand #0: [test.call_on_device]
335
336// IP:           operand #1: []
337// LOCAL:        operand #1: [callarg1]
338// LC_AW:        operand #1: [test.call_on_device]
339
340// IP:           operand #2: [callarg2]
341// LOCAL:        operand #2: [callarg2]
342// LC_AW:        operand #2: [test.call_on_device]
343
344// CHECK:        result #0: [b]
345func.func @test_call_on_device(%arg0: i32, %arg1: i32, %device: i32, %m0: memref<i32>) {
346  %0 = test.call_on_device @callee(%arg0, %arg1), %device {tag = "a"} : (i32, i32, i32) -> (i32)
347  memref.store %0, %m0[] {tag_name = "b"} : memref<i32>
348  return
349}
350
351// -----
352
353func.func private @external_callee(%arg0: i32) -> i32
354
355// CHECK-LABEL: test_tag: add_external
356// IP:    operand #0: [callarg0]
357// LOCAL: operand #0: [callarg0]
358// LC_AW: operand #0: [func.call]
359// IP_AW: operand #0: [func.call]
360
361func.func @test_external_callee(%arg0: i32, %m0: memref<i32>) {
362  %0 = arith.addi %arg0, %arg0 { tag = "add_external"}: i32
363  %1 = func.call @external_callee(%arg0) : (i32) -> i32
364  memref.store %1, %m0[] {tag_name = "a"} : memref<i32>
365  return
366}
367