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
10 // RUN: --test-next-access='interprocedural=false assume-func-reads=true' \
11 // RUN: --split-input-file | FileCheck %s --check-prefixes=CHECK,LC_AR
13 // Check prefixes are as follows:
14 // 'check': common for all runs;
15 // 'ip_ar': interpocedural runs assuming calls to external functions read
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.
21 // CHECK-LABEL: @trivial
22 func.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>
32 // CHECK-LABEL: @chain
33 func.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
47 // CHECK-LABEL: @branch
48 func.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
55 %0 = memref.load %arg0[] {name = "load 1"} : memref<f32>
59 %1 = memref.load %arg0[] {name = "load 2"} : memref<f32>
66 // CHECK-LABEL: @dead_branch
67 func.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>
74 // CHECK: name = "load 1"
75 // CHECK-SAME: next_access = "not computed"
76 %0 = memref.load %arg0[] {name = "load 1"} : memref<f32>
80 %1 = memref.load %arg0[] {name = "load 2"} : memref<f32>
88 func.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
100 %v = memref.load %arg0[%arg3] {name = "outside"} : memref<?xf32>
101 %2 = arith.addf %v, %l : f32
105 // CHECK-LABEL: @conditional
106 func.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>
111 // CHECK: name = "then"
112 // CHECK-SAME: next_access = {{\[}}["post"]]
113 memref.load %arg0[] {name = "then"} : memref<f32>
115 memref.load %arg0[] {name = "post"} : memref<f32>
119 // CHECK-LABEL: @two_sided_conditional
120 func.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>
125 // CHECK: name = "then"
126 // CHECK-SAME: next_access = {{\[}}["post"]]
127 memref.load %arg0[] {name = "then"} : memref<f32>
129 // CHECK: name = "else"
130 // CHECK-SAME: next_access = {{\[}}["post"]]
131 memref.load %arg0[] {name = "else"} : memref<f32>
133 memref.load %arg0[] {name = "post"} : memref<f32>
137 // CHECK-LABEL: @dead_conditional
138 func.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>
144 // CHECK: name = "then"
145 // CHECK-SAME: next_access = "not computed"
146 memref.load %arg0[] {name = "then"} : memref<f32>
148 memref.load %arg0[] {name = "post"} : memref<f32>
152 // CHECK-LABEL: @known_conditional
153 func.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>
159 // CHECK: name = "then"
160 // CHECK-SAME: next_access = "not computed"
161 memref.load %arg0[] {name = "then"} : memref<f32>
163 // CHECK: name = "else"
164 // CHECK-SAME: next_access = {{\[}}["post"]]
165 memref.load %arg0[] {name = "else"} : memref<f32>
167 memref.load %arg0[] {name = "post"} : memref<f32>
171 // CHECK-LABEL: @loop_cf
172 func.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
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)
189 %7 = memref.load %arg0[%arg3] {name = "outside"} : memref<?xf32>
190 %8 = arith.addf %7, %2 : f32
194 // CHECK-LABEL: @conditional_cf
195 func.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
201 // CHECK: name = "then"
202 // CHECK-SAME: next_access = {{\[}}["post"]]
203 %1 = memref.load %arg1[] {name = "then"} : memref<f32>
206 %2 = memref.load %arg1[] {name = "post"} : memref<f32>
210 // CHECK-LABEL: @two_sided_conditional_cf
211 func.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
217 // CHECK: name = "then"
218 // CHECK-SAME: next_access = {{\[}}["post"]]
219 %1 = memref.load %arg1[] {name = "then"} : memref<f32>
222 // CHECK: name = "else"
223 // CHECK-SAME: next_access = {{\[}}["post"]]
224 %2 = memref.load %arg1[] {name = "else"} : memref<f32>
227 %3 = memref.load %arg1[] {name = "post"} : memref<f32>
231 // CHECK-LABEL: @dead_conditional_cf
232 func.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
239 // CHECK: name = "then"
240 // CHECK-SAME: next_access = "not computed"
241 %1 = memref.load %arg0[] {name = "then"} : memref<f32>
244 %2 = memref.load %arg0[] {name = "post"} : memref<f32>
248 // CHECK-LABEL: @known_conditional_cf
249 func.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
256 // CHECK: name = "then"
257 // CHECK-SAME: next_access = "not computed"
258 %1 = memref.load %arg0[] {name = "then"} : memref<f32>
261 // CHECK: name = "else"
262 // CHECK-SAME: next_access = {{\[}}["post"]]
263 %2 = memref.load %arg0[] {name = "else"} : memref<f32>
266 %3 = memref.load %arg0[] {name = "post"} : memref<f32>
272 func.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>
281 func.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>
288 // CHECK-LABEL: @simple_call
289 func.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>
304 // CHECK-LABEL: @infinite_recursive_call
305 func.func @infinite_recursive_call(%arg0: memref<f32>) {
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>
320 // CHECK-LABEL: @recursive_call
321 func.func @recursive_call(%arg0: memref<f32>, %cond: i1) {
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>
330 func.call @recursive_call(%arg0, %cond) {name = "call"} : (memref<f32>, i1) -> ()
332 memref.load %arg0[] {name = "post"} : memref<f32>
338 // CHECK-LABEL: @recursive_call_cf
339 func.func @recursive_call_cf(%arg0: memref<f32>, %cond: i1) {
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
349 call @recursive_call_cf(%arg0, %cond) {name = "call"} : (memref<f32>, i1) -> ()
352 %2 = memref.load %arg0[] {name = "post"} : memref<f32>
358 func.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>
367 func.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>
376 func.func @conditonal_call(%arg0: memref<f32>, %cond: i1) {
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>
385 func.call @callee1(%arg0) {name = "call1"} : (memref<f32>) -> ()
387 func.call @callee2(%arg0) {name = "call2"} : (memref<f32>) -> ()
389 memref.load %arg0[] {name = "post"} : memref<f32>
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"
400 func.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>
409 // CHECK-LABEL: @call_and_store_before
410 func.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>
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"
434 func.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>
443 // CHECK-LABEL: @call_and_store_after
444 func.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>
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
468 func.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
478 memref.load %arg0[] {name = "post"} : memref<f32>
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
487 func.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
497 memref.load %arg0[] {name = "post"} : memref<f32>
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
511 func.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
524 // CHECK: name = "post"
525 // CHECK-SAME: next_access = ["unknown"]
526 memref.load %arg0[] {name = "post"} : memref<f32>
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
540 func.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
553 // CHECK: name = "post"
554 // CHECK-SAME: next_access = ["unknown"]
555 memref.load %arg0[] {name = "post"} : memref<f32>
561 func.func private @opaque_callee(%arg0: memref<f32>)
563 // CHECK-LABEL: @call_opaque_callee
564 func.func @call_opaque_callee(%arg0: memref<f32>) {
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>
581 // CHECK-LABEL: @indirect_call
582 func.func @indirect_call(%arg0: memref<f32>, %arg1: (memref<f32>) -> ()) {
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>