blob: 0827c6750189e58d2c074e9a82dc6685294a38a2 [file]
// Edge-case tests for the AccessPath infrastructure in fir::AliasAnalysis.
//
// These tests exercise scenarios that may arise when the IR is not in the
// canonical form produced by lowering (e.g. missing fir.declare, non-FIR ops
// in the def-use chain) and verify that alias analysis remains sound.
// RUN: fir-opt %s -split-input-file -o /dev/null --mlir-disable-threading \
// RUN: -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' \
// RUN: 2>&1 | FileCheck -match-full-lines %s
// -----
// Bare fir.alloca without fir.declare.
// Component disambiguation must still work because fir.coordinate_of sets
// approximateSource (its getViewOffset returns nullopt), which triggers
// pathsDivergeAtComponent.
// CHECK-LABEL: Testing : "_QPtest_bare_alloca"
// CHECK-DAG: comp_a#0 <-> comp_b#0: NoAlias
// CHECK-DAG: comp_a#0 <-> comp_a2#0: MayAlias
// CHECK-DAG: comp_b#0 <-> comp_a2#0: NoAlias
func.func @_QPtest_bare_alloca() {
%0 = fir.alloca !fir.type<_QMmTty{a:f32,b:f32}> {uniq_name = "_QFtestEobj"}
%a = fir.coordinate_of %0, a {test.ptr = "comp_a"} : (!fir.ref<!fir.type<_QMmTty{a:f32,b:f32}>>) -> !fir.ref<f32>
%b = fir.coordinate_of %0, b {test.ptr = "comp_b"} : (!fir.ref<!fir.type<_QMmTty{a:f32,b:f32}>>) -> !fir.ref<f32>
%a2 = fir.coordinate_of %0, a {test.ptr = "comp_a2"} : (!fir.ref<!fir.type<_QMmTty{a:f32,b:f32}>>) -> !fir.ref<f32>
%v1 = fir.load %a : !fir.ref<f32>
%v2 = fir.load %b : !fir.ref<f32>
%v3 = fir.load %a2 : !fir.ref<f32>
return
}
// -----
// Non-FIR operation (arith.select) breaking the def-use chain.
// The walk hits the Default handler, producing SourceKind::Unknown, so all
// pairs involving the broken chain must conservatively return MayAlias.
// The direct access through the original alloca is also MayAlias with the
// broken-chain accesses because their origins differ (Unknown vs Allocate).
// CHECK-LABEL: Testing : "_QPtest_unknown_op"
// CHECK-DAG: sel_comp_a#0 <-> sel_comp_b#0: MayAlias
// CHECK-DAG: sel_comp_a#0 <-> direct_comp_a#0: MayAlias
// CHECK-DAG: sel_comp_b#0 <-> direct_comp_a#0: MayAlias
func.func @_QPtest_unknown_op(%cond: i1) {
%0 = fir.alloca !fir.type<_QMmTty{a:f32,b:f32}> {uniq_name = "_QFtestEobj1"}
%1 = fir.alloca !fir.type<_QMmTty{a:f32,b:f32}> {uniq_name = "_QFtestEobj2"}
%sel = arith.select %cond, %0, %1 : !fir.ref<!fir.type<_QMmTty{a:f32,b:f32}>>
%a = fir.coordinate_of %sel, a {test.ptr = "sel_comp_a"} : (!fir.ref<!fir.type<_QMmTty{a:f32,b:f32}>>) -> !fir.ref<f32>
%b = fir.coordinate_of %sel, b {test.ptr = "sel_comp_b"} : (!fir.ref<!fir.type<_QMmTty{a:f32,b:f32}>>) -> !fir.ref<f32>
%direct_a = fir.coordinate_of %0, a {test.ptr = "direct_comp_a"} : (!fir.ref<!fir.type<_QMmTty{a:f32,b:f32}>>) -> !fir.ref<f32>
%v1 = fir.load %a : !fir.ref<f32>
%v2 = fir.load %b : !fir.ref<f32>
%v3 = fir.load %direct_a : !fir.ref<f32>
return
}
// -----
// Dynamic array index in fir.coordinate_of must NOT produce a Component step.
// Two subscripts of the same array component are MayAlias (could be the same
// element), but an array element vs a different named component is NoAlias.
// CHECK-LABEL: Testing : "_QPtest_dynamic_index"
// CHECK-DAG: arr_elem1#0 <-> arr_elem2#0: MayAlias
// CHECK-DAG: arr_elem1#0 <-> comp_b#0: NoAlias
// CHECK-DAG: arr_elem2#0 <-> comp_b#0: NoAlias
func.func @_QPtest_dynamic_index(%idx1: index, %idx2: index) {
%0 = fir.alloca !fir.type<_QMmTty2{arr:!fir.array<10xf32>,b:f32}> {uniq_name = "_QFtestEobj"}
%obj = fir.declare %0 {uniq_name = "_QFtestEobj"} : (!fir.ref<!fir.type<_QMmTty2{arr:!fir.array<10xf32>,b:f32}>>) -> !fir.ref<!fir.type<_QMmTty2{arr:!fir.array<10xf32>,b:f32}>>
%arrref = fir.coordinate_of %obj, arr : (!fir.ref<!fir.type<_QMmTty2{arr:!fir.array<10xf32>,b:f32}>>) -> !fir.ref<!fir.array<10xf32>>
%elem1 = fir.coordinate_of %arrref, %idx1 {test.ptr = "arr_elem1"} : (!fir.ref<!fir.array<10xf32>>, index) -> !fir.ref<f32>
%elem2 = fir.coordinate_of %arrref, %idx2 {test.ptr = "arr_elem2"} : (!fir.ref<!fir.array<10xf32>>, index) -> !fir.ref<f32>
%bref = fir.coordinate_of %obj, b {test.ptr = "comp_b"} : (!fir.ref<!fir.type<_QMmTty2{arr:!fir.array<10xf32>,b:f32}>>) -> !fir.ref<f32>
%v1 = fir.load %elem1 : !fir.ref<f32>
%v2 = fir.load %elem2 : !fir.ref<f32>
%v3 = fir.load %bref : !fir.ref<f32>
return
}
// -----
// Nested derived type: multi-level component paths.
// x%inner%a and x%inner%b diverge at the second Component step -> NoAlias.
// x%inner%a and x%j diverge at the first Component step -> NoAlias.
// CHECK-LABEL: Testing : "_QPtest_nested"
// CHECK-DAG: inner_a#0 <-> inner_b#0: NoAlias
// CHECK-DAG: inner_a#0 <-> outer_j#0: NoAlias
// CHECK-DAG: inner_b#0 <-> outer_j#0: NoAlias
func.func @_QPtest_nested() {
%0 = fir.alloca !fir.type<_QMmTouter{inner:!fir.type<_QMmTinner{a:f32,b:f32}>,j:f32}> {uniq_name = "_QFtestEobj"}
%obj = fir.declare %0 {uniq_name = "_QFtestEobj"} : (!fir.ref<!fir.type<_QMmTouter{inner:!fir.type<_QMmTinner{a:f32,b:f32}>,j:f32}>>) -> !fir.ref<!fir.type<_QMmTouter{inner:!fir.type<_QMmTinner{a:f32,b:f32}>,j:f32}>>
%inner = fir.coordinate_of %obj, inner : (!fir.ref<!fir.type<_QMmTouter{inner:!fir.type<_QMmTinner{a:f32,b:f32}>,j:f32}>>) -> !fir.ref<!fir.type<_QMmTinner{a:f32,b:f32}>>
%a = fir.coordinate_of %inner, a {test.ptr = "inner_a"} : (!fir.ref<!fir.type<_QMmTinner{a:f32,b:f32}>>) -> !fir.ref<f32>
%b = fir.coordinate_of %inner, b {test.ptr = "inner_b"} : (!fir.ref<!fir.type<_QMmTinner{a:f32,b:f32}>>) -> !fir.ref<f32>
%j = fir.coordinate_of %obj, j {test.ptr = "outer_j"} : (!fir.ref<!fir.type<_QMmTouter{inner:!fir.type<_QMmTinner{a:f32,b:f32}>,j:f32}>>) -> !fir.ref<f32>
%v1 = fir.load %a : !fir.ref<f32>
%v2 = fir.load %b : !fir.ref<f32>
%v3 = fir.load %j : !fir.ref<f32>
return
}
// -----
// Multi-field fir.coordinate_of accessing nested fields in a single operation.
// The access path must record a PathStep for each field index.
// CHECK-LABEL: Testing : "_QPtest_multi_field_coord"
// CHECK-DAG: mf_inner_a#0 <-> mf_inner_b#0: NoAlias
// CHECK-DAG: mf_inner_a#0 <-> mf_outer_j#0: NoAlias
// CHECK-DAG: mf_inner_b#0 <-> mf_outer_j#0: NoAlias
func.func @_QPtest_multi_field_coord() {
%0 = fir.alloca !fir.type<_QMmTouter{inner:!fir.type<_QMmTinner{a:f32,b:f32}>,j:f32}> {uniq_name = "_QFtestEobj"}
%obj = fir.declare %0 {uniq_name = "_QFtestEobj"} : (!fir.ref<!fir.type<_QMmTouter{inner:!fir.type<_QMmTinner{a:f32,b:f32}>,j:f32}>>) -> !fir.ref<!fir.type<_QMmTouter{inner:!fir.type<_QMmTinner{a:f32,b:f32}>,j:f32}>>
%a = fir.coordinate_of %obj, inner, a {test.ptr = "mf_inner_a"} : (!fir.ref<!fir.type<_QMmTouter{inner:!fir.type<_QMmTinner{a:f32,b:f32}>,j:f32}>>) -> !fir.ref<f32>
%b = fir.coordinate_of %obj, inner, b {test.ptr = "mf_inner_b"} : (!fir.ref<!fir.type<_QMmTouter{inner:!fir.type<_QMmTinner{a:f32,b:f32}>,j:f32}>>) -> !fir.ref<f32>
%j = fir.coordinate_of %obj, j {test.ptr = "mf_outer_j"} : (!fir.ref<!fir.type<_QMmTouter{inner:!fir.type<_QMmTinner{a:f32,b:f32}>,j:f32}>>) -> !fir.ref<f32>
%v1 = fir.load %a : !fir.ref<f32>
%v2 = fir.load %b : !fir.ref<f32>
%v3 = fir.load %j : !fir.ref<f32>
return
}
// -----
// Interleaved component and 1-D array index in a single fir.coordinate_of.
// The dynamic index (kDynamicIndex) must be consumed by the dimension peeling
// logic without producing a Component step.
// obj%arr_of_inner(idx)%a and obj%arr_of_inner(idx)%b diverge at the leaf
// component -> NoAlias. Both diverge from obj%j at the first step -> NoAlias.
// CHECK-LABEL: Testing : "_QPtest_interleaved_1d"
// CHECK-DAG: arr_inner_a#0 <-> arr_inner_b#0: NoAlias
// CHECK-DAG: arr_inner_a#0 <-> outer_j#0: NoAlias
// CHECK-DAG: arr_inner_b#0 <-> outer_j#0: NoAlias
func.func @_QPtest_interleaved_1d(%idx1: index, %idx2: index) {
%0 = fir.alloca !fir.type<_QMmTwrap{arr_of_inner:!fir.array<10x!fir.type<_QMmTinner{a:f32,b:f32}>>,j:f32}> {uniq_name = "_QFtestEobj"}
%obj = fir.declare %0 {uniq_name = "_QFtestEobj"} : (!fir.ref<!fir.type<_QMmTwrap{arr_of_inner:!fir.array<10x!fir.type<_QMmTinner{a:f32,b:f32}>>,j:f32}>>) -> !fir.ref<!fir.type<_QMmTwrap{arr_of_inner:!fir.array<10x!fir.type<_QMmTinner{a:f32,b:f32}>>,j:f32}>>
%a = fir.coordinate_of %obj, arr_of_inner, %idx1, a {test.ptr = "arr_inner_a"} : (!fir.ref<!fir.type<_QMmTwrap{arr_of_inner:!fir.array<10x!fir.type<_QMmTinner{a:f32,b:f32}>>,j:f32}>>, index) -> !fir.ref<f32>
%b = fir.coordinate_of %obj, arr_of_inner, %idx2, b {test.ptr = "arr_inner_b"} : (!fir.ref<!fir.type<_QMmTwrap{arr_of_inner:!fir.array<10x!fir.type<_QMmTinner{a:f32,b:f32}>>,j:f32}>>, index) -> !fir.ref<f32>
%j = fir.coordinate_of %obj, j {test.ptr = "outer_j"} : (!fir.ref<!fir.type<_QMmTwrap{arr_of_inner:!fir.array<10x!fir.type<_QMmTinner{a:f32,b:f32}>>,j:f32}>>) -> !fir.ref<f32>
%v1 = fir.load %a : !fir.ref<f32>
%v2 = fir.load %b : !fir.ref<f32>
%v3 = fir.load %j : !fir.ref<f32>
return
}
// -----
// Nested 2-D arrays: component, idx, idx, component, idx, idx.
// The dimension peeling loop must fire twice (once per array level), each
// time consuming two kDynamicIndex entries before advancing to the element type.
// obj%arr_of_inner(i,j)%x(k,l) and obj%arr_of_inner(i,j)%y(k,l) diverge at
// the second component -> NoAlias.
// CHECK-LABEL: Testing : "_QPtest_nested_2d_arrays"
// CHECK-DAG: nested_x#0 <-> nested_y#0: NoAlias
// CHECK-DAG: nested_x#0 <-> outer_k#0: NoAlias
// CHECK-DAG: nested_y#0 <-> outer_k#0: NoAlias
func.func @_QPtest_nested_2d_arrays(%idx1: index, %idx2: index, %idx3: index, %idx4: index, %idx5: index, %idx6: index, %idx7: index, %idx8: index) {
%0 = fir.alloca !fir.type<_QMmTouter2{arr_of_inner:!fir.array<10x20x!fir.type<_QMmTinner2{x:!fir.array<5x10xf32>,y:!fir.array<5x10xf32>}>>,k:f32}> {uniq_name = "_QFtestEobj"}
%obj = fir.declare %0 {uniq_name = "_QFtestEobj"} : (!fir.ref<!fir.type<_QMmTouter2{arr_of_inner:!fir.array<10x20x!fir.type<_QMmTinner2{x:!fir.array<5x10xf32>,y:!fir.array<5x10xf32>}>>,k:f32}>>) -> !fir.ref<!fir.type<_QMmTouter2{arr_of_inner:!fir.array<10x20x!fir.type<_QMmTinner2{x:!fir.array<5x10xf32>,y:!fir.array<5x10xf32>}>>,k:f32}>>
%x = fir.coordinate_of %obj, arr_of_inner, %idx1, %idx2, x, %idx3, %idx4 {test.ptr = "nested_x"} : (!fir.ref<!fir.type<_QMmTouter2{arr_of_inner:!fir.array<10x20x!fir.type<_QMmTinner2{x:!fir.array<5x10xf32>,y:!fir.array<5x10xf32>}>>,k:f32}>>, index, index, index, index) -> !fir.ref<f32>
%y = fir.coordinate_of %obj, arr_of_inner, %idx5, %idx6, y, %idx7, %idx8 {test.ptr = "nested_y"} : (!fir.ref<!fir.type<_QMmTouter2{arr_of_inner:!fir.array<10x20x!fir.type<_QMmTinner2{x:!fir.array<5x10xf32>,y:!fir.array<5x10xf32>}>>,k:f32}>>, index, index, index, index) -> !fir.ref<f32>
%k = fir.coordinate_of %obj, k {test.ptr = "outer_k"} : (!fir.ref<!fir.type<_QMmTouter2{arr_of_inner:!fir.array<10x20x!fir.type<_QMmTinner2{x:!fir.array<5x10xf32>,y:!fir.array<5x10xf32>}>>,k:f32}>>) -> !fir.ref<f32>
%v1 = fir.load %x : !fir.ref<f32>
%v2 = fir.load %y : !fir.ref<f32>
%v3 = fir.load %k : !fir.ref<f32>
return
}