xref: /llvm-project/mlir/test/Analysis/DataFlow/test-next-access.mlir (revision f0b0c02504899c1bc00c6e6428e7aebe9ea5beb1)
1// RUN: mlir-opt %s --test-next-access --split-input-file |\
2// RUN:             FileCheck %s --check-prefixes=CHECK,IP
3// RUN: mlir-opt %s --test-next-access='interprocedural=false' \
4// RUN:             --split-input-file |\
5// RUN:             FileCheck %s --check-prefixes=CHECK,LOCAL
6// RUN: mlir-opt %s --test-next-access='assume-func-reads=true' \
7// RUN:             --split-input-file |\
8// RUN:             FileCheck %s --check-prefixes=CHECK,IP_AR
9// RUN: mlir-opt %s \
10// RUN:      --test-next-access='interprocedural=false assume-func-reads=true' \
11// RUN:      --split-input-file | FileCheck %s --check-prefixes=CHECK,LC_AR
12
13// Check prefixes are as follows:
14// 'check': common for all runs;
15// 'ip_ar': interpocedural runs assuming calls to external functions read
16//          all arguments;
17// 'ip': interprocedural runs not assuming function calls reading;
18// 'local': local (non-interprocedural) analysis not assuming calls reading;
19// 'lc_ar': local analysis assuming external calls reading all arguments.
20
21// CHECK-LABEL: @trivial
22func.func @trivial(%arg0: memref<f32>, %arg1: f32) -> f32 {
23  // CHECK:      name = "store"
24  // CHECK-SAME: next_access = ["unknown", ["load"]]
25  memref.store %arg1, %arg0[] {name = "store"} : memref<f32>
26  // CHECK:      name = "load"
27  // CHECK-SAME: next_access = ["unknown"]
28  %0 = memref.load %arg0[] {name = "load"} : memref<f32>
29  return %0 : f32
30}
31
32// CHECK-LABEL: @chain
33func.func @chain(%arg0: memref<f32>, %arg1: f32) -> f32 {
34  // CHECK:      name = "store"
35  // CHECK-SAME: next_access = ["unknown", ["load 1"]]
36  memref.store %arg1, %arg0[] {name = "store"} : memref<f32>
37  // CHECK:      name = "load 1"
38  // CHECK-SAME: next_access = {{\[}}["load 2"]]
39  %0 = memref.load %arg0[] {name = "load 1"} : memref<f32>
40  // CHECK:      name = "load 2"
41  // CHECK-SAME: next_access = ["unknown"]
42  %1 = memref.load %arg0[] {name = "load 2"} : memref<f32>
43  %2 = arith.addf %0, %1 : f32
44  return %2 : f32
45}
46
47// CHECK-LABEL: @branch
48func.func @branch(%arg0: memref<f32>, %arg1: f32, %arg2: i1) -> f32 {
49  // CHECK:      name = "store"
50  // CHECK-SAME: next_access = ["unknown", ["load 1", "load 2"]]
51  memref.store %arg1, %arg0[] {name = "store"} : memref<f32>
52  cf.cond_br %arg2, ^bb0, ^bb1
53
54^bb0:
55  %0 = memref.load %arg0[] {name = "load 1"} : memref<f32>
56  cf.br ^bb2(%0 : f32)
57
58^bb1:
59  %1 = memref.load %arg0[] {name = "load 2"} : memref<f32>
60  cf.br ^bb2(%1 : f32)
61
62^bb2(%phi: f32):
63  return %phi : f32
64}
65
66// CHECK-LABEL: @dead_branch
67func.func @dead_branch(%arg0: memref<f32>, %arg1: f32) -> f32 {
68  // CHECK:      name = "store"
69  // CHECK-SAME: next_access = ["unknown", ["load 2"]]
70  memref.store %arg1, %arg0[] {name = "store"} : memref<f32>
71  cf.br ^bb1
72
73^bb0:
74  // CHECK:      name = "load 1"
75  // CHECK-SAME: next_access = "not computed"
76  %0 = memref.load %arg0[] {name = "load 1"} : memref<f32>
77  cf.br ^bb2(%0 : f32)
78
79^bb1:
80  %1 = memref.load %arg0[] {name = "load 2"} : memref<f32>
81  cf.br ^bb2(%1 : f32)
82
83^bb2(%phi: f32):
84  return %phi : f32
85}
86
87// CHECK-LABEL: @loop
88func.func @loop(%arg0: memref<?xf32>, %arg1: f32, %arg2: index, %arg3: index, %arg4: index) -> f32 {
89  %c0 = arith.constant 0.0 : f32
90  // CHECK:      name = "pre"
91  // CHECK-SAME: next_access = {{\[}}["outside", "loop"], "unknown"]
92  memref.load %arg0[%arg4] {name = "pre"} : memref<?xf32>
93  %l = scf.for %i = %arg2 to %arg3 step %arg4 iter_args(%ia = %c0) -> (f32) {
94    // CHECK:      name = "loop"
95    // CHECK-SAME: next_access = {{\[}}["outside", "loop"], "unknown"]
96    %0 = memref.load %arg0[%i] {name = "loop"} : memref<?xf32>
97    %1 = arith.addf %ia, %0 : f32
98    scf.yield %1 : f32
99  }
100  %v = memref.load %arg0[%arg3] {name = "outside"} : memref<?xf32>
101  %2 = arith.addf %v, %l : f32
102  return %2 : f32
103}
104
105// CHECK-LABEL: @conditional
106func.func @conditional(%cond: i1, %arg0: memref<f32>) {
107  // CHECK:      name = "pre"
108  // CHECK-SAME: next_access = {{\[}}["post", "then"]]
109  memref.load %arg0[] {name = "pre"}: memref<f32>
110  scf.if %cond {
111    // CHECK:      name = "then"
112    // CHECK-SAME: next_access = {{\[}}["post"]]
113    memref.load %arg0[] {name = "then"} : memref<f32>
114  }
115  memref.load %arg0[] {name = "post"} : memref<f32>
116  return
117}
118
119// CHECK-LABEL: @two_sided_conditional
120func.func @two_sided_conditional(%cond: i1, %arg0: memref<f32>) {
121  // CHECK:      name = "pre"
122  // CHECK-SAME: next_access = {{\[}}["then", "else"]]
123  memref.load %arg0[] {name = "pre"}: memref<f32>
124  scf.if %cond {
125    // CHECK:      name = "then"
126    // CHECK-SAME: next_access = {{\[}}["post"]]
127    memref.load %arg0[] {name = "then"} : memref<f32>
128  } else {
129    // CHECK:      name = "else"
130    // CHECK-SAME: next_access = {{\[}}["post"]]
131    memref.load %arg0[] {name = "else"} : memref<f32>
132  }
133  memref.load %arg0[] {name = "post"} : memref<f32>
134  return
135}
136
137// CHECK-LABEL: @dead_conditional
138func.func @dead_conditional(%arg0: memref<f32>) {
139  %false = arith.constant 0 : i1
140  // CHECK:      name = "pre"
141  // CHECK-SAME: next_access = {{\[}}["post"]]
142  memref.load %arg0[] {name = "pre"}: memref<f32>
143  scf.if %false {
144    // CHECK:      name = "then"
145    // CHECK-SAME: next_access = "not computed"
146    memref.load %arg0[] {name = "then"} : memref<f32>
147  }
148  memref.load %arg0[] {name = "post"} : memref<f32>
149  return
150}
151
152// CHECK-LABEL: @known_conditional
153func.func @known_conditional(%arg0: memref<f32>) {
154  %false = arith.constant 0 : i1
155  // CHECK:      name = "pre"
156  // CHECK-SAME: next_access = {{\[}}["else"]]
157  memref.load %arg0[] {name = "pre"}: memref<f32>
158  scf.if %false {
159    // CHECK:      name = "then"
160    // CHECK-SAME: next_access = "not computed"
161    memref.load %arg0[] {name = "then"} : memref<f32>
162  } else {
163    // CHECK:      name = "else"
164    // CHECK-SAME: next_access = {{\[}}["post"]]
165    memref.load %arg0[] {name = "else"} : memref<f32>
166  }
167  memref.load %arg0[] {name = "post"} : memref<f32>
168  return
169}
170
171// CHECK-LABEL: @loop_cf
172func.func @loop_cf(%arg0: memref<?xf32>, %arg1: f32, %arg2: index, %arg3: index, %arg4: index) -> f32 {
173  %cst = arith.constant 0.000000e+00 : f32
174  // CHECK:      name = "pre"
175  // CHECK-SAME: next_access = {{\[}}["loop", "outside"], "unknown"]
176  %0 = memref.load %arg0[%arg4] {name = "pre"} : memref<?xf32>
177  cf.br ^bb1(%arg2, %cst : index, f32)
178^bb1(%1: index, %2: f32):
179  %3 = arith.cmpi slt, %1, %arg3 : index
180  cf.cond_br %3, ^bb2, ^bb3
181^bb2:
182  // CHECK:      name = "loop"
183  // CHECK-SAME: next_access = {{\[}}["loop", "outside"], "unknown"]
184  %4 = memref.load %arg0[%1] {name = "loop"} : memref<?xf32>
185  %5 = arith.addf %2, %4 : f32
186  %6 = arith.addi %1, %arg4 : index
187  cf.br ^bb1(%6, %5 : index, f32)
188^bb3:
189  %7 = memref.load %arg0[%arg3] {name = "outside"} : memref<?xf32>
190  %8 = arith.addf %7, %2 : f32
191  return %8 : f32
192}
193
194// CHECK-LABEL: @conditional_cf
195func.func @conditional_cf(%arg0: i1, %arg1: memref<f32>) {
196  // CHECK:      name = "pre"
197  // CHECK-SAME: next_access = {{\[}}["then", "post"]]
198  %0 = memref.load %arg1[] {name = "pre"} : memref<f32>
199  cf.cond_br %arg0, ^bb1, ^bb2
200^bb1:
201  // CHECK:      name = "then"
202  // CHECK-SAME: next_access = {{\[}}["post"]]
203  %1 = memref.load %arg1[] {name = "then"} : memref<f32>
204  cf.br ^bb2
205^bb2:
206  %2 = memref.load %arg1[] {name = "post"} : memref<f32>
207  return
208}
209
210// CHECK-LABEL: @two_sided_conditional_cf
211func.func @two_sided_conditional_cf(%arg0: i1, %arg1: memref<f32>) {
212  // CHECK:      name = "pre"
213  // CHECK-SAME: next_access = {{\[}}["then", "else"]]
214  %0 = memref.load %arg1[] {name = "pre"} : memref<f32>
215  cf.cond_br %arg0, ^bb1, ^bb2
216^bb1:
217  // CHECK:      name = "then"
218  // CHECK-SAME: next_access = {{\[}}["post"]]
219  %1 = memref.load %arg1[] {name = "then"} : memref<f32>
220  cf.br ^bb3
221^bb2:
222  // CHECK:      name = "else"
223  // CHECK-SAME: next_access = {{\[}}["post"]]
224  %2 = memref.load %arg1[] {name = "else"} : memref<f32>
225  cf.br ^bb3
226^bb3:
227  %3 = memref.load %arg1[] {name = "post"} : memref<f32>
228  return
229}
230
231// CHECK-LABEL: @dead_conditional_cf
232func.func @dead_conditional_cf(%arg0: memref<f32>) {
233  %false = arith.constant false
234  // CHECK:      name = "pre"
235  // CHECK-SAME: next_access = {{\[}}["post"]]
236  %0 = memref.load %arg0[] {name = "pre"} : memref<f32>
237  cf.cond_br %false, ^bb1, ^bb2
238^bb1:
239  // CHECK:      name = "then"
240  // CHECK-SAME: next_access = "not computed"
241  %1 = memref.load %arg0[] {name = "then"} : memref<f32>
242  cf.br ^bb2
243^bb2:
244  %2 = memref.load %arg0[] {name = "post"} : memref<f32>
245  return
246}
247
248// CHECK-LABEL: @known_conditional_cf
249func.func @known_conditional_cf(%arg0: memref<f32>) {
250  %false = arith.constant false
251  // CHECK:      name = "pre"
252  // CHECK-SAME: next_access = {{\[}}["else"]]
253  %0 = memref.load %arg0[] {name = "pre"} : memref<f32>
254  cf.cond_br %false, ^bb1, ^bb2
255^bb1:
256  // CHECK:      name = "then"
257  // CHECK-SAME: next_access = "not computed"
258  %1 = memref.load %arg0[] {name = "then"} : memref<f32>
259  cf.br ^bb3
260^bb2:
261  // CHECK:      name = "else"
262  // CHECK-SAME: next_access = {{\[}}["post"]]
263  %2 = memref.load %arg0[] {name = "else"} : memref<f32>
264  cf.br ^bb3
265^bb3:
266  %3 = memref.load %arg0[] {name = "post"} : memref<f32>
267  return
268}
269
270// -----
271
272func.func private @callee1(%arg0: memref<f32>) {
273  // IP:         name = "callee1"
274  // IP-SAME:    next_access = {{\[}}["post"]]
275  // LOCAL:      name = "callee1"
276  // LOCAL-SAME: next_access = ["unknown"]
277  memref.load %arg0[] {name = "callee1"} : memref<f32>
278  return
279}
280
281func.func private @callee2(%arg0: memref<f32>) {
282  // CHECK:      name = "callee2"
283  // CHECK-SAME: next_access = "not computed"
284  memref.load %arg0[] {name = "callee2"} : memref<f32>
285  return
286}
287
288// CHECK-LABEL: @simple_call
289func.func @simple_call(%arg0: memref<f32>) {
290  // IP:         name = "caller"
291  // IP-SAME:    next_access = {{\[}}["callee1"]]
292  // LOCAL:      name = "caller"
293  // LOCAL-SAME: next_access = ["unknown"]
294  // LC_AR:      name = "caller"
295  // LC_AR-SAME: next_access = {{\[}}["call"]]
296  memref.load %arg0[] {name = "caller"} : memref<f32>
297  func.call @callee1(%arg0) {name = "call"} : (memref<f32>) -> ()
298  memref.load %arg0[] {name = "post"} : memref<f32>
299  return
300}
301
302// -----
303
304// CHECK-LABEL: @infinite_recursive_call
305func.func @infinite_recursive_call(%arg0: memref<f32>) {
306  // IP:         name = "pre"
307  // IP-SAME:    next_access = {{\[}}["pre"]]
308  // LOCAL:      name = "pre"
309  // LOCAL-SAME: next_access = ["unknown"]
310  // LC_AR:      name = "pre"
311  // LC_AR-SAME: next_access = {{\[}}["call"]]
312  memref.load %arg0[] {name = "pre"} : memref<f32>
313  func.call @infinite_recursive_call(%arg0) {name = "call"} : (memref<f32>) -> ()
314  memref.load %arg0[] {name = "post"} : memref<f32>
315  return
316}
317
318// -----
319
320// CHECK-LABEL: @recursive_call
321func.func @recursive_call(%arg0: memref<f32>, %cond: i1) {
322  // IP:         name = "pre"
323  // IP-SAME:    next_access = {{\[}}["post", "pre"]]
324  // LOCAL:      name = "pre"
325  // LOCAL-SAME: next_access = ["unknown"]
326  // LC_AR:      name = "pre"
327  // LC_AR-SAME: next_access = {{\[}}["post", "call"]]
328  memref.load %arg0[] {name = "pre"} : memref<f32>
329  scf.if %cond {
330    func.call @recursive_call(%arg0, %cond) {name = "call"} : (memref<f32>, i1) -> ()
331  }
332  memref.load %arg0[] {name = "post"} : memref<f32>
333  return
334}
335
336// -----
337
338// CHECK-LABEL: @recursive_call_cf
339func.func @recursive_call_cf(%arg0: memref<f32>, %cond: i1) {
340  // IP:         name = "pre"
341  // IP-SAME:    next_access = {{\[}}["pre", "post"]]
342  // LOCAL:      name = "pre"
343  // LOCAL-SAME: next_access = ["unknown"]
344  // LC_AR:      name = "pre"
345  // LC_AR-SAME: next_access = {{\[}}["call", "post"]]
346  %0 = memref.load %arg0[] {name = "pre"} : memref<f32>
347  cf.cond_br %cond, ^bb1, ^bb2
348^bb1:
349  call @recursive_call_cf(%arg0, %cond) {name = "call"} : (memref<f32>, i1) -> ()
350  cf.br ^bb2
351^bb2:
352  %2 = memref.load %arg0[] {name = "post"} : memref<f32>
353  return
354}
355
356// -----
357
358func.func private @callee1(%arg0: memref<f32>) {
359  // IP:         name = "callee1"
360  // IP-SAME:    next_access = {{\[}}["post"]]
361  // LOCAL:      name = "callee1"
362  // LOCAL-SAME: next_access = ["unknown"]
363  memref.load %arg0[] {name = "callee1"} : memref<f32>
364  return
365}
366
367func.func private @callee2(%arg0: memref<f32>) {
368  // IP:         name = "callee2"
369  // IP-SAME:    next_access = {{\[}}["post"]]
370  // LOCAL:      name = "callee2"
371  // LOCAL-SAME: next_access = ["unknown"]
372  memref.load %arg0[] {name = "callee2"} : memref<f32>
373  return
374}
375
376func.func @conditonal_call(%arg0: memref<f32>, %cond: i1) {
377  // IP:         name = "pre"
378  // IP-SAME:    next_access = {{\[}}["callee1", "callee2"]]
379  // LOCAL:      name = "pre"
380  // LOCAL-SAME: next_access = ["unknown"]
381  // LC_AR:      name = "pre"
382  // LC_AR-SAME: next_access = {{\[}}["call1", "call2"]]
383  memref.load %arg0[] {name = "pre"} : memref<f32>
384  scf.if %cond {
385    func.call @callee1(%arg0) {name = "call1"} : (memref<f32>) -> ()
386  } else {
387    func.call @callee2(%arg0) {name = "call2"} : (memref<f32>) -> ()
388  }
389  memref.load %arg0[] {name = "post"} : memref<f32>
390  return
391}
392
393// -----
394
395
396// In this test, the "call" operation also accesses %arg0 itself before
397// transferring control flow to the callee. Therefore, the order of accesses is
398// "caller" -> "call" -> "callee" -> "post"
399
400func.func private @callee(%arg0: memref<f32>) {
401  // IP:         name = "callee"
402  // IP-SAME:    next_access = {{\[}}["post"]]
403  // LOCAL:      name = "callee"
404  // LOCAL-SAME: next_access = ["unknown"]
405  memref.load %arg0[] {name = "callee"} : memref<f32>
406  return
407}
408
409// CHECK-LABEL: @call_and_store_before
410func.func @call_and_store_before(%arg0: memref<f32>) {
411  // IP:         name = "caller"
412  // IP-SAME:    next_access = {{\[}}["call"]]
413  // LOCAL:      name = "caller"
414  // LOCAL-SAME: next_access = ["unknown"]
415  // LC_AR:      name = "caller"
416  // LC_AR-SAME: next_access = {{\[}}["call"]]
417  memref.load %arg0[] {name = "caller"} : memref<f32>
418  // Note that the access after the entire call is "post".
419  // CHECK:      name = "call"
420  // CHECK-SAME: next_access = {{\[}}["post"], ["post"]]
421  test.call_and_store @callee(%arg0), %arg0 {name = "call", store_before_call = true} : (memref<f32>, memref<f32>) -> ()
422  // CHECK:      name = "post"
423  // CHECK-SAME: next_access = ["unknown"]
424  memref.load %arg0[] {name = "post"} : memref<f32>
425  return
426}
427
428// -----
429
430// In this test, the "call" operation also accesses %arg0 itself after getting
431// control flow back from the callee. Therefore, the order of accesses is
432// "caller" -> "callee" -> "call" -> "post"
433
434func.func private @callee(%arg0: memref<f32>) {
435  // IP:         name = "callee"
436  // IP-SAME:    next_access = {{\[}}["call"]]
437  // LOCAL:      name = "callee"
438  // LOCAL-SAME: next_access = ["unknown"]
439  memref.load %arg0[] {name = "callee"} : memref<f32>
440  return
441}
442
443// CHECK-LABEL: @call_and_store_after
444func.func @call_and_store_after(%arg0: memref<f32>) {
445  // IP:         name = "caller"
446  // IP-SAME:    next_access = {{\[}}["callee"]]
447  // LOCAL:      name = "caller"
448  // LOCAL-SAME: next_access = ["unknown"]
449  // LC_AR:      name = "caller"
450  // LC_AR-SAME: next_access = {{\[}}["call"]]
451  memref.load %arg0[] {name = "caller"} : memref<f32>
452  // CHECK:      name = "call"
453  // CHECK-SAME: next_access = {{\[}}["post"], ["post"]]
454  test.call_and_store @callee(%arg0), %arg0 {name = "call", store_before_call = false} : (memref<f32>, memref<f32>) -> ()
455  // CHECK:      name = "post"
456  // CHECK-SAME: next_access = ["unknown"]
457  memref.load %arg0[] {name = "post"} : memref<f32>
458  return
459}
460
461// -----
462
463// In this test, the "region" operation also accesses %arg0 itself before
464// entering the region. Therefore:
465//   - the next access of "pre" is the "region" operation itself;
466//   - at the entry of the block, the next access is "post".
467// CHECK-LABEL: @store_with_a_region
468func.func @store_with_a_region_before(%arg0: memref<f32>) {
469  // CHECK:      name = "pre"
470  // CHECK-SAME: next_access = {{\[}}["region"]]
471  memref.load %arg0[] {name = "pre"} : memref<f32>
472  // CHECK:              name = "region"
473  // CHECK-SAME: next_access = {{\[}}["post"]]
474  // CHECK-SAME: next_at_entry_point = {{\[}}{{\[}}["post"]]]
475  test.store_with_a_region %arg0 attributes { name = "region", store_before_region = true } {
476    test.store_with_a_region_terminator
477  } : memref<f32>
478  memref.load %arg0[] {name = "post"} : memref<f32>
479  return
480}
481
482// In this test, the "region" operation also accesses %arg0 itself after
483// exiting from the region. Therefore:
484//   - the next access of "pre" is the "region" operation itself;
485//   - at the entry of the block, the next access is "region".
486// CHECK-LABEL: @store_with_a_region
487func.func @store_with_a_region_after(%arg0: memref<f32>) {
488  // CHECK:      name = "pre"
489  // CHECK-SAME: next_access = {{\[}}["region"]]
490  memref.load %arg0[] {name = "pre"} : memref<f32>
491  // CHECK:      name = "region"
492  // CHECK-SAME: next_access = {{\[}}["post"]]
493  // CHECK-SAME: next_at_entry_point = {{\[}}{{\[}}["region"]]]
494  test.store_with_a_region %arg0 attributes { name = "region", store_before_region = false } {
495    test.store_with_a_region_terminator
496  } : memref<f32>
497  memref.load %arg0[] {name = "post"} : memref<f32>
498  return
499}
500
501// In this test, the operation with a region stores to %arg0 before going to the
502// region. Therefore:
503//   - the next access of "pre" is the "region" operation itself;
504//   - the next access of the "region" operation (computed as the next access
505//     *after* said operation) is the "post" operation;
506//   - the next access of the "inner" operation is also "post";
507//   - the next access at the entry point of the region of the "region" operation
508//     is the "inner" operation.
509// That is, the order of access is: "pre" -> "region" -> "inner" -> "post".
510// CHECK-LABEL: @store_with_a_region_before_containing_a_load
511func.func @store_with_a_region_before_containing_a_load(%arg0: memref<f32>) {
512  // CHECK:      name = "pre"
513  // CHECK-SAME: next_access = {{\[}}["region"]]
514  memref.load %arg0[] {name = "pre"} : memref<f32>
515  // CHECK:      name = "region"
516  // CHECK-SAME: next_access = {{\[}}["post"]]
517  // CHECK-SAME: next_at_entry_point = {{\[}}{{\[}}["inner"]]]
518  test.store_with_a_region %arg0 attributes { name = "region", store_before_region = true } {
519    // CHECK:      name = "inner"
520    // CHECK-SAME: next_access = {{\[}}["post"]]
521    memref.load %arg0[] {name = "inner"} : memref<f32>
522    test.store_with_a_region_terminator
523  } : memref<f32>
524  // CHECK:      name = "post"
525  // CHECK-SAME: next_access = ["unknown"]
526  memref.load %arg0[] {name = "post"} : memref<f32>
527  return
528}
529
530// In this test, the operation with a region stores to %arg0 after exiting from
531// the region. Therefore:
532//   - the next access of "pre" is "inner";
533//   - the next access of the "region" operation (computed as the next access
534//     *after* said operation) is the "post" operation);
535//   - the next access at the entry point of the region of the "region" operation
536//     is the "inner" operation;
537//   - the next access of the "inner" operation is the "region" operation itself.
538// That is, the order of access is "pre" -> "inner" -> "region" -> "post".
539// CHECK-LABEL: @store_with_a_region_after_containing_a_load
540func.func @store_with_a_region_after_containing_a_load(%arg0: memref<f32>) {
541  // CHECK:      name = "pre"
542  // CHECK-SAME: next_access = {{\[}}["inner"]]
543  memref.load %arg0[] {name = "pre"} : memref<f32>
544  // CHECK:      name = "region"
545  // CHECK-SAME: next_access = {{\[}}["post"]]
546  // CHECK-SAME: next_at_entry_point = {{\[}}{{\[}}["inner"]]]
547  test.store_with_a_region %arg0 attributes { name = "region", store_before_region = false } {
548    // CHECK:      name = "inner"
549    // CHECK-SAME: next_access = {{\[}}["region"]]
550    memref.load %arg0[] {name = "inner"} : memref<f32>
551    test.store_with_a_region_terminator
552  } : memref<f32>
553  // CHECK:      name = "post"
554  // CHECK-SAME: next_access = ["unknown"]
555  memref.load %arg0[] {name = "post"} : memref<f32>
556  return
557}
558
559// -----
560
561func.func private @opaque_callee(%arg0: memref<f32>)
562
563// CHECK-LABEL: @call_opaque_callee
564func.func @call_opaque_callee(%arg0: memref<f32>) {
565  // IP:         name = "pre"
566  // IP-SAME:    next_access = ["unknown"]
567  // IP_AR:      name = "pre"
568  // IP_AR-SAME: next_access = {{\[}}["call"]]
569  // LOCAL:      name = "pre"
570  // LOCAL-SAME: next_access = ["unknown"]
571  // LC_AR:      name = "pre"
572  // LC_AR-SAME: next_access = {{\[}}["call"]]
573  memref.load %arg0[] {name = "pre"} : memref<f32>
574  func.call @opaque_callee(%arg0) {name = "call"} : (memref<f32>) -> ()
575  memref.load %arg0[] {name = "post"} : memref<f32>
576  return
577}
578
579// -----
580
581// CHECK-LABEL: @indirect_call
582func.func @indirect_call(%arg0: memref<f32>, %arg1: (memref<f32>) -> ()) {
583  // IP:         name = "pre"
584  // IP-SAME:    next_access = ["unknown"]
585  // IP_AR:      name = "pre"
586  // IP_AR-SAME: next_access = ["unknown"]
587  // LOCAL:      name = "pre"
588  // LOCAL-SAME: next_access = ["unknown"]
589  // LC_AR:      name = "pre"
590  // LC_AR-SAME: next_access = {{\[}}["call"]]
591  memref.load %arg0[] {name = "pre"} : memref<f32>
592  func.call_indirect %arg1(%arg0) {name = "call"} : (memref<f32>) -> ()
593  memref.load %arg0[] {name = "post"} : memref<f32>
594  return
595}
596