xref: /llvm-project/mlir/test/Analysis/DataFlow/test-last-modified-callgraph.mlir (revision 32a4e3fccaf304c8d541bdefdb1a7ef829f84c1c)
1// RUN: mlir-opt -test-last-modified --split-input-file %s 2>&1 |\
2// RUN:          FileCheck %s --check-prefixes=CHECK,IP,IP_ONLY
3// RUN: mlir-opt -test-last-modified='assume-func-writes=true' \
4// RUN:          --split-input-file %s 2>&1 |\
5// RUN:          FileCheck %s --check-prefixes=CHECK,IP,IP_AW
6// RUN: mlir-opt -test-last-modified='interprocedural=false' \
7// RUN:          --split-input-file %s 2>&1 |\
8// RUN:          FileCheck %s --check-prefixes=CHECK,LOCAL
9// RUN: mlir-opt \
10// RUN:    -test-last-modified='interprocedural=false assume-func-writes=true' \
11// RUN:    --split-input-file %s 2>&1 |\
12// RUN:    FileCheck %s --check-prefixes=CHECK,LC_AW
13
14// Check prefixes are as follows:
15// 'check': common for all runs;
16// 'ip': interprocedural runs;
17// 'ip_aw': interpocedural runs assuming calls to external functions write to
18//          all arguments;
19// 'ip_only': interprocedural runs not assuming calls writing;
20// 'local': local (non-interprocedural) analysis not assuming calls writing;
21// 'lc_aw': local analysis assuming external calls writing to all arguments.
22
23// CHECK-LABEL: test_tag: test_callsite
24// IP:    operand #0
25// IP-NEXT: - a
26// LOCAL: operand #0
27// LOCAL-NEXT: - <unknown>
28// LC_AW: operand #0
29// LC_AW-NEXT: - <unknown>
30func.func private @single_callsite_fn(%ptr: memref<i32>) -> memref<i32> {
31  return {tag = "test_callsite"} %ptr : memref<i32>
32}
33
34func.func @test_callsite() {
35  %ptr = memref.alloc() : memref<i32>
36  %c0 = arith.constant 0 : i32
37  memref.store %c0, %ptr[] {tag_name = "a"} : memref<i32>
38  %0 = func.call @single_callsite_fn(%ptr) : (memref<i32>) -> memref<i32>
39  return
40}
41
42// CHECK-LABEL: test_tag: test_return_site
43// IP:    operand #0
44// IP-NEXT:    - b
45// LOCAL: operand #0
46// LOCAL-NEXT: - <unknown>
47// LC_AW: operand #0
48// LC_AW-NEXT: - <unknown>
49func.func private @single_return_site_fn(%ptr: memref<i32>) -> memref<i32> {
50  %c0 = arith.constant 0 : i32
51  memref.store %c0, %ptr[] {tag_name = "b"} : memref<i32>
52  return %ptr : memref<i32>
53}
54
55// CHECK-LABEL: test_tag: test_multiple_callsites
56// IP:    operand #0
57// IP-NEXT:    write0
58// IP-NEXT:    write1
59// LOCAL: operand #0
60// LOCAL-NEXT: - <unknown>
61// LC_AW: operand #0
62// LC_AW-NEXT: - <unknown>
63func.func @test_return_site(%ptr: memref<i32>) -> memref<i32> {
64  %0 = func.call @single_return_site_fn(%ptr) : (memref<i32>) -> memref<i32>
65  return {tag = "test_return_site"} %0 : memref<i32>
66}
67
68func.func private @multiple_callsite_fn(%ptr: memref<i32>) -> memref<i32> {
69  return {tag = "test_multiple_callsites"} %ptr : memref<i32>
70}
71
72func.func @test_multiple_callsites(%a: i32, %ptr: memref<i32>) -> memref<i32> {
73  memref.store %a, %ptr[] {tag_name = "write0"} : memref<i32>
74  %0 = func.call @multiple_callsite_fn(%ptr) : (memref<i32>) -> memref<i32>
75  memref.store %a, %ptr[] {tag_name = "write1"} : memref<i32>
76  %1 = func.call @multiple_callsite_fn(%ptr) : (memref<i32>) -> memref<i32>
77  return %ptr : memref<i32>
78}
79
80// CHECK-LABEL: test_tag: test_multiple_return_sites
81// IP:    operand #0
82// IP-NEXT:    return0
83// IP-NEXT:    return1
84// LOCAL: operand #0
85// LOCAL-NEXT: - <unknown>
86// LC_AW: operand #0
87// LC_AW-NEXT: - <unknown>
88func.func private @multiple_return_site_fn(%cond: i1, %a: i32, %ptr: memref<i32>) -> memref<i32> {
89  cf.cond_br %cond, ^a, ^b
90
91^a:
92  memref.store %a, %ptr[] {tag_name = "return0"} : memref<i32>
93  return %ptr : memref<i32>
94
95^b:
96  memref.store %a, %ptr[] {tag_name = "return1"} : memref<i32>
97  return %ptr : memref<i32>
98}
99
100func.func @test_multiple_return_sites(%cond: i1, %a: i32, %ptr: memref<i32>) -> memref<i32> {
101  %0 = func.call @multiple_return_site_fn(%cond, %a, %ptr) : (i1, i32, memref<i32>) -> memref<i32>
102  return {tag = "test_multiple_return_sites"} %0 : memref<i32>
103}
104
105// -----
106
107// CHECK-LABEL: test_tag: after_call
108// IP:    operand #0
109// IP-NEXT:    - write0
110// LOCAL: operand #0
111// LOCAL-NEXT: - <unknown>
112// LC_AW: operand #0
113// LC_AW-NEXT: - func.call
114func.func private @void_return(%ptr: memref<i32>) {
115  return
116}
117
118func.func @test_call_void_return() {
119  %ptr = memref.alloc() : memref<i32>
120  %c0 = arith.constant 0 : i32
121  memref.store %c0, %ptr[] {tag_name = "write0"} : memref<i32>
122  func.call @void_return(%ptr) : (memref<i32>) -> ()
123  memref.load %ptr[] {tag = "after_call"} : memref<i32>
124  return
125}
126
127// -----
128
129func.func private @callee(%arg0: memref<f32>) -> memref<f32> {
130  %2 = arith.constant 2.0 : f32
131  memref.load %arg0[] {tag = "call_and_store_before::enter_callee"} : memref<f32>
132  memref.store %2, %arg0[] {tag_name = "callee"} : memref<f32>
133  memref.load %arg0[] {tag = "exit_callee"} : memref<f32>
134  return %arg0 : memref<f32>
135}
136// In this test, the "call" operation also stores to %arg0 itself before
137// transferring control flow to the callee. Therefore, the order of accesses is
138// "pre" -> "call" -> "callee" -> "post"
139
140// CHECK-LABEL: test_tag: call_and_store_before::enter_callee:
141// IP:     operand #0
142// IP:      - call
143// LOCAL:  operand #0
144// LOCAL:   - <unknown>
145// LC_AW:  operand #0
146// LC_AW:   - <unknown>
147
148// CHECK: test_tag: exit_callee:
149// CHECK:  operand #0
150// CHECK:   - callee
151
152// CHECK: test_tag: before_call:
153// CHECK:  operand #0
154// CHECK:   - pre
155
156// CHECK: test_tag: after_call:
157// IP:     operand #0
158// IP:      - callee
159// LOCAL:  operand #0
160// LOCAL:   - <unknown>
161// LC_AW:  operand #0
162// LC_AW:   - call
163
164// CHECK: test_tag: return:
165// CHECK:  operand #0
166// CHECK:   - post
167func.func @call_and_store_before(%arg0: memref<f32>) -> memref<f32> {
168  %0 = arith.constant 0.0 : f32
169  %1 = arith.constant 1.0 : f32
170  memref.store %0, %arg0[] {tag_name = "pre"} : memref<f32>
171  memref.load %arg0[] {tag = "before_call"} : memref<f32>
172  test.call_and_store @callee(%arg0), %arg0 {tag_name = "call", store_before_call = true} : (memref<f32>, memref<f32>) -> ()
173  memref.load %arg0[] {tag = "after_call"} : memref<f32>
174  memref.store %1, %arg0[] {tag_name = "post"} : memref<f32>
175  return {tag = "return"} %arg0 : memref<f32>
176}
177
178// -----
179
180func.func private @callee(%arg0: memref<f32>) -> memref<f32> {
181  %2 = arith.constant 2.0 : f32
182  memref.load %arg0[] {tag = "call_and_store_after::enter_callee"} : memref<f32>
183  memref.store %2, %arg0[] {tag_name = "callee"} : memref<f32>
184  memref.load %arg0[] {tag = "exit_callee"} : memref<f32>
185  return %arg0 : memref<f32>
186}
187
188// In this test, the "call" operation also stores to %arg0 itself after getting
189// control flow back from the callee. Therefore, the order of accesses is
190// "pre" -> "callee" -> "call" -> "post"
191
192// CHECK-LABEL: test_tag: call_and_store_after::enter_callee:
193// IP:     operand #0
194// IP:      - pre
195// LOCAL:  operand #0
196// LOCAL:   - <unknown>
197// LC_AW:  operand #0
198// LC_AW:   - <unknown>
199
200// CHECK: test_tag: exit_callee:
201// CHECK:  operand #0
202// CHECK:   - callee
203
204// CHECK: test_tag: before_call:
205// CHECK:  operand #0
206// CHECK:   - pre
207
208// CHECK:    test_tag: after_call:
209// IP:     operand #0
210// IP:      - call
211// LOCAL:  operand #0
212// LOCAL:   - <unknown>
213// LC_AW:  operand #0
214// LC_AW:   - call
215
216// CHECK: test_tag: return:
217// CHECK:  operand #0
218// CHECK:   - post
219func.func @call_and_store_after(%arg0: memref<f32>) -> memref<f32> {
220  %0 = arith.constant 0.0 : f32
221  %1 = arith.constant 1.0 : f32
222  memref.store %0, %arg0[] {tag_name = "pre"} : memref<f32>
223  memref.load %arg0[] {tag = "before_call"} : memref<f32>
224  test.call_and_store @callee(%arg0), %arg0 {tag_name = "call", store_before_call = false} : (memref<f32>, memref<f32>) -> ()
225  memref.load %arg0[] {tag = "after_call"} : memref<f32>
226  memref.store %1, %arg0[] {tag_name = "post"} : memref<f32>
227  return {tag = "return"} %arg0 : memref<f32>
228}
229
230// -----
231
232func.func private @void_return(%ptr: memref<i32>)
233
234// CHECK-LABEL: test_tag: after_opaque_call:
235// CHECK:        operand #0
236// IP_ONLY:       - <unknown>
237// IP_AW:         - func.call
238func.func @test_opaque_call_return() {
239  %ptr = memref.alloc() : memref<i32>
240  %c0 = arith.constant 0 : i32
241  memref.store %c0, %ptr[] {tag_name = "write0"} : memref<i32>
242  func.call @void_return(%ptr) : (memref<i32>) -> ()
243  memref.load %ptr[] {tag = "after_opaque_call"} : memref<i32>
244  return
245}
246