595 lines
21 KiB
MLIR
595 lines
21 KiB
MLIR
// RUN: mlir-opt %s --test-next-access --split-input-file |\
|
|
// RUN: FileCheck %s --check-prefixes=CHECK,IP
|
|
// RUN: mlir-opt %s --test-next-access='interprocedural=false' \
|
|
// RUN: --split-input-file |\
|
|
// RUN: FileCheck %s --check-prefixes=CHECK,LOCAL
|
|
// RUN: mlir-opt %s --test-next-access='assume-func-reads=true' \
|
|
// RUN: --split-input-file |\
|
|
// RUN: FileCheck %s --check-prefixes=CHECK,IP_AR
|
|
// RUN: mlir-opt %s \
|
|
// RUN: --test-next-access='interprocedural=false assume-func-reads=true' \
|
|
// RUN: --split-input-file | FileCheck %s --check-prefixes=CHECK,LC_AR
|
|
|
|
// Check prefixes are as follows:
|
|
// 'check': common for all runs;
|
|
// 'ip_ar': interpocedural runs assuming calls to external functions read
|
|
// all arguments;
|
|
// 'ip': interprocedural runs not assuming function calls reading;
|
|
// 'local': local (non-interprocedural) analysis not assuming calls reading;
|
|
// 'lc_ar': local analysis assuming external calls reading all arguments.
|
|
|
|
// CHECK-LABEL: @trivial
|
|
func.func @trivial(%arg0: memref<f32>, %arg1: f32) -> f32 {
|
|
// CHECK: name = "store"
|
|
// CHECK-SAME: next_access = ["unknown", ["load"]]
|
|
memref.store %arg1, %arg0[] {name = "store"} : memref<f32>
|
|
// CHECK: name = "load"
|
|
// CHECK-SAME: next_access = ["unknown"]
|
|
%0 = memref.load %arg0[] {name = "load"} : memref<f32>
|
|
return %0 : f32
|
|
}
|
|
|
|
// CHECK-LABEL: @chain
|
|
func.func @chain(%arg0: memref<f32>, %arg1: f32) -> f32 {
|
|
// CHECK: name = "store"
|
|
// CHECK-SAME: next_access = ["unknown", ["load 1"]]
|
|
memref.store %arg1, %arg0[] {name = "store"} : memref<f32>
|
|
// CHECK: name = "load 1"
|
|
// CHECK-SAME: next_access = {{\[}}["load 2"]]
|
|
%0 = memref.load %arg0[] {name = "load 1"} : memref<f32>
|
|
// CHECK: name = "load 2"
|
|
// CHECK-SAME: next_access = ["unknown"]
|
|
%1 = memref.load %arg0[] {name = "load 2"} : memref<f32>
|
|
%2 = arith.addf %0, %1 : f32
|
|
return %2 : f32
|
|
}
|
|
|
|
// CHECK-LABEL: @branch
|
|
func.func @branch(%arg0: memref<f32>, %arg1: f32, %arg2: i1) -> f32 {
|
|
// CHECK: name = "store"
|
|
// CHECK-SAME: next_access = ["unknown", ["load 1", "load 2"]]
|
|
memref.store %arg1, %arg0[] {name = "store"} : memref<f32>
|
|
cf.cond_br %arg2, ^bb0, ^bb1
|
|
|
|
^bb0:
|
|
%0 = memref.load %arg0[] {name = "load 1"} : memref<f32>
|
|
cf.br ^bb2(%0 : f32)
|
|
|
|
^bb1:
|
|
%1 = memref.load %arg0[] {name = "load 2"} : memref<f32>
|
|
cf.br ^bb2(%1 : f32)
|
|
|
|
^bb2(%phi: f32):
|
|
return %phi : f32
|
|
}
|
|
|
|
// CHECK-LABEL @dead_branch
|
|
func.func @dead_branch(%arg0: memref<f32>, %arg1: f32) -> f32 {
|
|
// CHECK: name = "store"
|
|
// CHECK-SAME: next_access = ["unknown", ["load 2"]]
|
|
memref.store %arg1, %arg0[] {name = "store"} : memref<f32>
|
|
cf.br ^bb1
|
|
|
|
^bb0:
|
|
// CHECK: name = "load 1"
|
|
// CHECK-SAME: next_access = "not computed"
|
|
%0 = memref.load %arg0[] {name = "load 1"} : memref<f32>
|
|
cf.br ^bb2(%0 : f32)
|
|
|
|
^bb1:
|
|
%1 = memref.load %arg0[] {name = "load 2"} : memref<f32>
|
|
cf.br ^bb2(%1 : f32)
|
|
|
|
^bb2(%phi: f32):
|
|
return %phi : f32
|
|
}
|
|
|
|
// CHECK-LABEL: @loop
|
|
func.func @loop(%arg0: memref<?xf32>, %arg1: f32, %arg2: index, %arg3: index, %arg4: index) -> f32 {
|
|
%c0 = arith.constant 0.0 : f32
|
|
// CHECK: name = "pre"
|
|
// CHECK-SAME: next_access = {{\[}}["outside", "loop"], "unknown"]
|
|
memref.load %arg0[%arg4] {name = "pre"} : memref<?xf32>
|
|
%l = scf.for %i = %arg2 to %arg3 step %arg4 iter_args(%ia = %c0) -> (f32) {
|
|
// CHECK: name = "loop"
|
|
// CHECK-SAME: next_access = {{\[}}["outside", "loop"], "unknown"]
|
|
%0 = memref.load %arg0[%i] {name = "loop"} : memref<?xf32>
|
|
%1 = arith.addf %ia, %0 : f32
|
|
scf.yield %1 : f32
|
|
}
|
|
%v = memref.load %arg0[%arg3] {name = "outside"} : memref<?xf32>
|
|
%2 = arith.addf %v, %l : f32
|
|
return %2 : f32
|
|
}
|
|
|
|
// CHECK-LABEL: @conditional
|
|
func.func @conditional(%cond: i1, %arg0: memref<f32>) {
|
|
// CHECK: name = "pre"
|
|
// CHECK-SAME: next_access = {{\[}}["post", "then"]]
|
|
memref.load %arg0[] {name = "pre"}: memref<f32>
|
|
scf.if %cond {
|
|
// CHECK: name = "then"
|
|
// CHECK-SAME: next_access = {{\[}}["post"]]
|
|
memref.load %arg0[] {name = "then"} : memref<f32>
|
|
}
|
|
memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @two_sided_conditional
|
|
func.func @two_sided_conditional(%cond: i1, %arg0: memref<f32>) {
|
|
// CHECK: name = "pre"
|
|
// CHECK-SAME: next_access = {{\[}}["then", "else"]]
|
|
memref.load %arg0[] {name = "pre"}: memref<f32>
|
|
scf.if %cond {
|
|
// CHECK: name = "then"
|
|
// CHECK-SAME: next_access = {{\[}}["post"]]
|
|
memref.load %arg0[] {name = "then"} : memref<f32>
|
|
} else {
|
|
// CHECK: name = "else"
|
|
// CHECK-SAME: next_access = {{\[}}["post"]]
|
|
memref.load %arg0[] {name = "else"} : memref<f32>
|
|
}
|
|
memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @dead_conditional
|
|
func.func @dead_conditional(%arg0: memref<f32>) {
|
|
%false = arith.constant 0 : i1
|
|
// CHECK: name = "pre"
|
|
// CHECK-SAME: next_access = {{\[}}["post"]]
|
|
memref.load %arg0[] {name = "pre"}: memref<f32>
|
|
scf.if %false {
|
|
// CHECK: name = "then"
|
|
// CHECK-SAME: next_access = "not computed"
|
|
memref.load %arg0[] {name = "then"} : memref<f32>
|
|
}
|
|
memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @known_conditional
|
|
func.func @known_conditional(%arg0: memref<f32>) {
|
|
%false = arith.constant 0 : i1
|
|
// CHECK: name = "pre"
|
|
// CHECK-SAME: next_access = {{\[}}["else"]]
|
|
memref.load %arg0[] {name = "pre"}: memref<f32>
|
|
scf.if %false {
|
|
// CHECK: name = "then"
|
|
// CHECK-SAME: next_access = "not computed"
|
|
memref.load %arg0[] {name = "then"} : memref<f32>
|
|
} else {
|
|
// CHECK: name = "else"
|
|
// CHECK-SAME: next_access = {{\[}}["post"]]
|
|
memref.load %arg0[] {name = "else"} : memref<f32>
|
|
}
|
|
memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @loop_cf
|
|
func.func @loop_cf(%arg0: memref<?xf32>, %arg1: f32, %arg2: index, %arg3: index, %arg4: index) -> f32 {
|
|
%cst = arith.constant 0.000000e+00 : f32
|
|
// CHECK: name = "pre"
|
|
// CHECK-SAME: next_access = {{\[}}["loop", "outside"], "unknown"]
|
|
%0 = memref.load %arg0[%arg4] {name = "pre"} : memref<?xf32>
|
|
cf.br ^bb1(%arg2, %cst : index, f32)
|
|
^bb1(%1: index, %2: f32):
|
|
%3 = arith.cmpi slt, %1, %arg3 : index
|
|
cf.cond_br %3, ^bb2, ^bb3
|
|
^bb2:
|
|
// CHECK: name = "loop"
|
|
// CHECK-SAME: next_access = {{\[}}["loop", "outside"], "unknown"]
|
|
%4 = memref.load %arg0[%1] {name = "loop"} : memref<?xf32>
|
|
%5 = arith.addf %2, %4 : f32
|
|
%6 = arith.addi %1, %arg4 : index
|
|
cf.br ^bb1(%6, %5 : index, f32)
|
|
^bb3:
|
|
%7 = memref.load %arg0[%arg3] {name = "outside"} : memref<?xf32>
|
|
%8 = arith.addf %7, %2 : f32
|
|
return %8 : f32
|
|
}
|
|
|
|
// CHECK-LABEL @conditional_cf
|
|
func.func @conditional_cf(%arg0: i1, %arg1: memref<f32>) {
|
|
// CHECK: name = "pre"
|
|
// CHECK-SAME: next_access = {{\[}}["then", "post"]]
|
|
%0 = memref.load %arg1[] {name = "pre"} : memref<f32>
|
|
cf.cond_br %arg0, ^bb1, ^bb2
|
|
^bb1:
|
|
// CHECK: name = "then"
|
|
// CHECK-SAME: next_access = {{\[}}["post"]]
|
|
%1 = memref.load %arg1[] {name = "then"} : memref<f32>
|
|
cf.br ^bb2
|
|
^bb2:
|
|
%2 = memref.load %arg1[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @two_sided_conditional_cf
|
|
func.func @two_sided_conditional_cf(%arg0: i1, %arg1: memref<f32>) {
|
|
// CHECK: name = "pre"
|
|
// CHECK-SAME: next_access = {{\[}}["then", "else"]]
|
|
%0 = memref.load %arg1[] {name = "pre"} : memref<f32>
|
|
cf.cond_br %arg0, ^bb1, ^bb2
|
|
^bb1:
|
|
// CHECK: name = "then"
|
|
// CHECK-SAME: next_access = {{\[}}["post"]]
|
|
%1 = memref.load %arg1[] {name = "then"} : memref<f32>
|
|
cf.br ^bb3
|
|
^bb2:
|
|
// CHECK: name = "else"
|
|
// CHECK-SAME: next_access = {{\[}}["post"]]
|
|
%2 = memref.load %arg1[] {name = "else"} : memref<f32>
|
|
cf.br ^bb3
|
|
^bb3:
|
|
%3 = memref.load %arg1[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @dead_conditional_cf
|
|
func.func @dead_conditional_cf(%arg0: memref<f32>) {
|
|
%false = arith.constant false
|
|
// CHECK: name = "pre"
|
|
// CHECK-SAME: next_access = {{\[}}["post"]]
|
|
%0 = memref.load %arg0[] {name = "pre"} : memref<f32>
|
|
cf.cond_br %false, ^bb1, ^bb2
|
|
^bb1:
|
|
// CHECK: name = "then"
|
|
// CHECK-SAME: next_access = "not computed"
|
|
%1 = memref.load %arg0[] {name = "then"} : memref<f32>
|
|
cf.br ^bb2
|
|
^bb2:
|
|
%2 = memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @known_conditional_cf
|
|
func.func @known_conditional_cf(%arg0: memref<f32>) {
|
|
%false = arith.constant false
|
|
// CHECK: name = "pre"
|
|
// CHECK-SAME: next_access = {{\[}}["else"]]
|
|
%0 = memref.load %arg0[] {name = "pre"} : memref<f32>
|
|
cf.cond_br %false, ^bb1, ^bb2
|
|
^bb1:
|
|
// CHECK: name = "then"
|
|
// CHECK-SAME: next_access = "not computed"
|
|
%1 = memref.load %arg0[] {name = "then"} : memref<f32>
|
|
cf.br ^bb3
|
|
^bb2:
|
|
// CHECK: name = "else"
|
|
// CHECK-SAME: next_access = {{\[}}["post"]]
|
|
%2 = memref.load %arg0[] {name = "else"} : memref<f32>
|
|
cf.br ^bb3
|
|
^bb3:
|
|
%3 = memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func private @callee1(%arg0: memref<f32>) {
|
|
// IP: name = "callee1"
|
|
// IP-SAME: next_access = {{\[}}["post"]]
|
|
// LOCAL: name = "callee1"
|
|
// LOCAL-SAME: next_access = ["unknown"]
|
|
memref.load %arg0[] {name = "callee1"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
func.func private @callee2(%arg0: memref<f32>) {
|
|
// CHECK: name = "callee2"
|
|
// CHECK-SAME: next_access = "not computed"
|
|
memref.load %arg0[] {name = "callee2"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @simple_call
|
|
func.func @simple_call(%arg0: memref<f32>) {
|
|
// IP: name = "caller"
|
|
// IP-SAME: next_access = {{\[}}["callee1"]]
|
|
// LOCAL: name = "caller"
|
|
// LOCAL-SAME: next_access = ["unknown"]
|
|
// LC_AR: name = "caller"
|
|
// LC_AR-SAME: next_access = {{\[}}["call"]]
|
|
memref.load %arg0[] {name = "caller"} : memref<f32>
|
|
func.call @callee1(%arg0) {name = "call"} : (memref<f32>) -> ()
|
|
memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
// CHECK-LABEL: @infinite_recursive_call
|
|
func.func @infinite_recursive_call(%arg0: memref<f32>) {
|
|
// IP: name = "pre"
|
|
// IP-SAME: next_access = {{\[}}["pre"]]
|
|
// LOCAL: name = "pre"
|
|
// LOCAL-SAME: next_access = ["unknown"]
|
|
// LC_AR: name = "pre"
|
|
// LC_AR-SAME: next_access = {{\[}}["call"]]
|
|
memref.load %arg0[] {name = "pre"} : memref<f32>
|
|
func.call @infinite_recursive_call(%arg0) {name = "call"} : (memref<f32>) -> ()
|
|
memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
// CHECK-LABEL: @recursive_call
|
|
func.func @recursive_call(%arg0: memref<f32>, %cond: i1) {
|
|
// IP: name = "pre"
|
|
// IP-SAME: next_access = {{\[}}["post", "pre"]]
|
|
// LOCAL: name = "pre"
|
|
// LOCAL-SAME: next_access = ["unknown"]
|
|
// LC_AR: name = "pre"
|
|
// LC_AR-SAME: next_access = {{\[}}["post", "call"]]
|
|
memref.load %arg0[] {name = "pre"} : memref<f32>
|
|
scf.if %cond {
|
|
func.call @recursive_call(%arg0, %cond) {name = "call"} : (memref<f32>, i1) -> ()
|
|
}
|
|
memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
// CHECK-LABEL: @recursive_call_cf
|
|
func.func @recursive_call_cf(%arg0: memref<f32>, %cond: i1) {
|
|
// IP: name = "pre"
|
|
// IP-SAME: next_access = {{\[}}["pre", "post"]]
|
|
// LOCAL: name = "pre"
|
|
// LOCAL-SAME: next_access = ["unknown"]
|
|
// LC_AR: name = "pre"
|
|
// LC_AR-SAME: next_access = {{\[}}["call", "post"]]
|
|
%0 = memref.load %arg0[] {name = "pre"} : memref<f32>
|
|
cf.cond_br %cond, ^bb1, ^bb2
|
|
^bb1:
|
|
call @recursive_call_cf(%arg0, %cond) {name = "call"} : (memref<f32>, i1) -> ()
|
|
cf.br ^bb2
|
|
^bb2:
|
|
%2 = memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func private @callee1(%arg0: memref<f32>) {
|
|
// IP: name = "callee1"
|
|
// IP-SAME: next_access = {{\[}}["post"]]
|
|
// LOCAL: name = "callee1"
|
|
// LOCAL-SAME: next_access = ["unknown"]
|
|
memref.load %arg0[] {name = "callee1"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
func.func private @callee2(%arg0: memref<f32>) {
|
|
// IP: name = "callee2"
|
|
// IP-SAME: next_access = {{\[}}["post"]]
|
|
// LOCAL: name = "callee2"
|
|
// LOCAL-SAME: next_access = ["unknown"]
|
|
memref.load %arg0[] {name = "callee2"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
func.func @conditonal_call(%arg0: memref<f32>, %cond: i1) {
|
|
// IP: name = "pre"
|
|
// IP-SAME: next_access = {{\[}}["callee1", "callee2"]]
|
|
// LOCAL: name = "pre"
|
|
// LOCAL-SAME: next_access = ["unknown"]
|
|
// LC_AR: name = "pre"
|
|
// LC_AR-SAME: next_access = {{\[}}["call1", "call2"]]
|
|
memref.load %arg0[] {name = "pre"} : memref<f32>
|
|
scf.if %cond {
|
|
func.call @callee1(%arg0) {name = "call1"} : (memref<f32>) -> ()
|
|
} else {
|
|
func.call @callee2(%arg0) {name = "call2"} : (memref<f32>) -> ()
|
|
}
|
|
memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
|
|
// In this test, the "call" operation also accesses %arg0 itself before
|
|
// transferring control flow to the callee. Therefore, the order of accesses is
|
|
// "caller" -> "call" -> "callee" -> "post"
|
|
|
|
func.func private @callee(%arg0: memref<f32>) {
|
|
// IP: name = "callee"
|
|
// IP-SAME: next_access = {{\[}}["post"]]
|
|
// LOCAL: name = "callee"
|
|
// LOCAL-SAME: next_access = ["unknown"]
|
|
memref.load %arg0[] {name = "callee"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @call_and_store_before
|
|
func.func @call_and_store_before(%arg0: memref<f32>) {
|
|
// IP: name = "caller"
|
|
// IP-SAME: next_access = {{\[}}["call"]]
|
|
// LOCAL: name = "caller"
|
|
// LOCAL-SAME: next_access = ["unknown"]
|
|
// LC_AR: name = "caller"
|
|
// LC_AR-SAME: next_access = {{\[}}["call"]]
|
|
memref.load %arg0[] {name = "caller"} : memref<f32>
|
|
// Note that the access after the entire call is "post".
|
|
// CHECK: name = "call"
|
|
// CHECK-SAME: next_access = {{\[}}["post"], ["post"]]
|
|
test.call_and_store @callee(%arg0), %arg0 {name = "call", store_before_call = true} : (memref<f32>, memref<f32>) -> ()
|
|
// CHECK: name = "post"
|
|
// CHECK-SAME: next_access = ["unknown"]
|
|
memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
// In this test, the "call" operation also accesses %arg0 itself after getting
|
|
// control flow back from the callee. Therefore, the order of accesses is
|
|
// "caller" -> "callee" -> "call" -> "post"
|
|
|
|
func.func private @callee(%arg0: memref<f32>) {
|
|
// IP: name = "callee"
|
|
// IP-SAME: next_access = {{\[}}["call"]]
|
|
// LOCAL: name = "callee"
|
|
// LOCAL-SAME: next_access = ["unknown"]
|
|
memref.load %arg0[] {name = "callee"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// CHECK-LABEL: @call_and_store_after
|
|
func.func @call_and_store_after(%arg0: memref<f32>) {
|
|
// IP: name = "caller"
|
|
// IP-SAME: next_access = {{\[}}["callee"]]
|
|
// LOCAL: name = "caller"
|
|
// LOCAL-SAME: next_access = ["unknown"]
|
|
// LC_AR: name = "caller"
|
|
// LC_AR-SAME: next_access = {{\[}}["call"]]
|
|
memref.load %arg0[] {name = "caller"} : memref<f32>
|
|
// CHECK: name = "call"
|
|
// CHECK-SAME: next_access = {{\[}}["post"], ["post"]]
|
|
test.call_and_store @callee(%arg0), %arg0 {name = "call", store_before_call = false} : (memref<f32>, memref<f32>) -> ()
|
|
// CHECK: name = "post"
|
|
// CHECK-SAME: next_access = ["unknown"]
|
|
memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
// In this test, the "region" operation also accesses %arg0 itself before
|
|
// entering the region. Therefore:
|
|
// - the next access of "pre" is the "region" operation itself;
|
|
// - at the entry of the block, the next access is "post".
|
|
// CHECK-LABEL: @store_with_a_region
|
|
func.func @store_with_a_region_before(%arg0: memref<f32>) {
|
|
// CHECK: name = "pre"
|
|
// CHECK-SAME: next_access = {{\[}}["region"]]
|
|
memref.load %arg0[] {name = "pre"} : memref<f32>
|
|
// CHECK: name = "region"
|
|
// CHECK-SAME: next_access = {{\[}}["post"]]
|
|
// CHECK-SAME: next_at_entry_point = {{\[}}{{\[}}["post"]]]
|
|
test.store_with_a_region %arg0 attributes { name = "region", store_before_region = true } {
|
|
test.store_with_a_region_terminator
|
|
} : memref<f32>
|
|
memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// In this test, the "region" operation also accesses %arg0 itself after
|
|
// exiting from the region. Therefore:
|
|
// - the next access of "pre" is the "region" operation itself;
|
|
// - at the entry of the block, the next access is "region".
|
|
// CHECK-LABEL: @store_with_a_region
|
|
func.func @store_with_a_region_after(%arg0: memref<f32>) {
|
|
// CHECK: name = "pre"
|
|
// CHECK-SAME: next_access = {{\[}}["region"]]
|
|
memref.load %arg0[] {name = "pre"} : memref<f32>
|
|
// CHECK: name = "region"
|
|
// CHECK-SAME: next_access = {{\[}}["post"]]
|
|
// CHECK-SAME: next_at_entry_point = {{\[}}{{\[}}["region"]]]
|
|
test.store_with_a_region %arg0 attributes { name = "region", store_before_region = false } {
|
|
test.store_with_a_region_terminator
|
|
} : memref<f32>
|
|
memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// In this test, the operation with a region stores to %arg0 before going to the
|
|
// region. Therefore:
|
|
// - the next access of "pre" is the "region" operation itself;
|
|
// - the next access of the "region" operation (computed as the next access
|
|
// *after* said operation) is the "post" operation;
|
|
// - the next access of the "inner" operation is also "post";
|
|
// - the next access at the entry point of the region of the "region" operation
|
|
// is the "inner" operation.
|
|
// That is, the order of access is: "pre" -> "region" -> "inner" -> "post".
|
|
// CHECK-LABEL: @store_with_a_region_before_containing_a_load
|
|
func.func @store_with_a_region_before_containing_a_load(%arg0: memref<f32>) {
|
|
// CHECK: name = "pre"
|
|
// CHECK-SAME: next_access = {{\[}}["region"]]
|
|
memref.load %arg0[] {name = "pre"} : memref<f32>
|
|
// CHECK: name = "region"
|
|
// CHECK-SAME: next_access = {{\[}}["post"]]
|
|
// CHECK-SAME: next_at_entry_point = {{\[}}{{\[}}["inner"]]]
|
|
test.store_with_a_region %arg0 attributes { name = "region", store_before_region = true } {
|
|
// CHECK: name = "inner"
|
|
// CHECK-SAME: next_access = {{\[}}["post"]]
|
|
memref.load %arg0[] {name = "inner"} : memref<f32>
|
|
test.store_with_a_region_terminator
|
|
} : memref<f32>
|
|
// CHECK: name = "post"
|
|
// CHECK-SAME: next_access = ["unknown"]
|
|
memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// In this test, the operation with a region stores to %arg0 after exiting from
|
|
// the region. Therefore:
|
|
// - the next access of "pre" is "inner";
|
|
// - the next access of the "region" operation (computed as the next access
|
|
// *after* said operation) is the "post" operation);
|
|
// - the next access at the entry point of the region of the "region" operation
|
|
// is the "inner" operation;
|
|
// - the next access of the "inner" operation is the "region" operation itself.
|
|
// That is, the order of access is "pre" -> "inner" -> "region" -> "post".
|
|
// CHECK-LABEL: @store_with_a_region_after_containing_a_load
|
|
func.func @store_with_a_region_after_containing_a_load(%arg0: memref<f32>) {
|
|
// CHECK: name = "pre"
|
|
// CHECK-SAME: next_access = {{\[}}["inner"]]
|
|
memref.load %arg0[] {name = "pre"} : memref<f32>
|
|
// CHECK: name = "region"
|
|
// CHECK-SAME: next_access = {{\[}}["post"]]
|
|
// CHECK-SAME: next_at_entry_point = {{\[}}{{\[}}["inner"]]]
|
|
test.store_with_a_region %arg0 attributes { name = "region", store_before_region = false } {
|
|
// CHECK: name = "inner"
|
|
// CHECK-SAME: next_access = {{\[}}["region"]]
|
|
memref.load %arg0[] {name = "inner"} : memref<f32>
|
|
test.store_with_a_region_terminator
|
|
} : memref<f32>
|
|
// CHECK: name = "post"
|
|
// CHECK-SAME: next_access = ["unknown"]
|
|
memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
func.func private @opaque_callee(%arg0: memref<f32>)
|
|
|
|
// CHECK-LABEL: @call_opaque_callee
|
|
func.func @call_opaque_callee(%arg0: memref<f32>) {
|
|
// IP: name = "pre"
|
|
// IP-SAME: next_access = ["unknown"]
|
|
// IP_AR: name = "pre"
|
|
// IP_AR-SAME: next_access = {{\[}}["call"]]
|
|
// LOCAL: name = "pre"
|
|
// LOCAL-SAME: next_access = ["unknown"]
|
|
// LC_AR: name = "pre"
|
|
// LC_AR-SAME: next_access = {{\[}}["call"]]
|
|
memref.load %arg0[] {name = "pre"} : memref<f32>
|
|
func.call @opaque_callee(%arg0) {name = "call"} : (memref<f32>) -> ()
|
|
memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|
|
|
|
// -----
|
|
|
|
// CHECK-LABEL: @indirect_call
|
|
func.func @indirect_call(%arg0: memref<f32>, %arg1: (memref<f32>) -> ()) {
|
|
// IP: name = "pre"
|
|
// IP-SAME: next_access = ["unknown"]
|
|
// IP_AR: name = "pre"
|
|
// IP_AR-SAME: next_access = ["unknown"]
|
|
// LOCAL: name = "pre"
|
|
// LOCAL-SAME: next_access = ["unknown"]
|
|
// LC_AR: name = "pre"
|
|
// LC_AR-SAME: next_access = {{\[}}["call"]]
|
|
memref.load %arg0[] {name = "pre"} : memref<f32>
|
|
func.call_indirect %arg1(%arg0) {name = "call"} : (memref<f32>) -> ()
|
|
memref.load %arg0[] {name = "post"} : memref<f32>
|
|
return
|
|
}
|