diff --git a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h index 8ab5150cd7c81..97c64dc341952 100644 --- a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h +++ b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h @@ -157,6 +157,26 @@ struct AliasAnalysis { bool isData() const; bool isBoxData() const; + /// @name Dummy Argument Aliasing + /// + /// Check conditions related to dummy argument aliasing. + /// + /// For all uses, a result of false can prevent MayAlias from being + /// reported, so the list of cases where false is returned is conservative. + + ///@{ + /// The address of a (possibly host associated) dummy argument of the + /// current function? + bool mayBeDummyArgOrHostAssoc() const; + /// \c mayBeDummyArgOrHostAssoc and the address of a pointer? + bool mayBePtrDummyArgOrHostAssoc() const; + /// The address of an actual argument of the current function? + bool mayBeActualArg() const; + /// \c mayBeActualArg and the address of either a pointer or a composite + /// with a pointer component? + bool mayBeActualArgWithPtr(const mlir::Value *val) const; + ///@} + mlir::Type getType() const; }; diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp index 6ee4f0ff71057..241803c3938a6 100644 --- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp +++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp @@ -96,6 +96,39 @@ bool AliasAnalysis::Source::isBoxData() const { origin.isData; } +bool AliasAnalysis::Source::mayBeDummyArgOrHostAssoc() const { + return kind != SourceKind::Allocate && kind != SourceKind::Global; +} + +bool AliasAnalysis::Source::mayBePtrDummyArgOrHostAssoc() const { + // Must alias like dummy arg (or HostAssoc). + if (!mayBeDummyArgOrHostAssoc()) + return false; + // Must be address of the dummy arg not of a dummy arg component. + if (isRecordWithPointerComponent(valueType)) + return false; + // Must be address *of* (not *in*) a pointer. + return attributes.test(Attribute::Pointer) && !isData(); +} + +bool AliasAnalysis::Source::mayBeActualArg() const { + return kind != SourceKind::Allocate; +} + +bool AliasAnalysis::Source::mayBeActualArgWithPtr( + const mlir::Value *val) const { + // Must not be local. + if (!mayBeActualArg()) + return false; + // Can be address *of* (not *in*) a pointer. + if (attributes.test(Attribute::Pointer) && !isData()) + return true; + // Can be address of a composite with a pointer component. + if (isRecordWithPointerComponent(val->getType())) + return true; + return false; +} + AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) { // TODO: alias() has to be aware of the function scopes. // After MLIR inlining, the current implementation may @@ -118,6 +151,10 @@ AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) { } if (lhsSrc.kind == rhsSrc.kind) { + // If the kinds and origins are the same, then lhs and rhs must alias unless + // either source is approximate. Approximate sources are for parts of the + // origin, but we don't have info here on which parts and whether they + // overlap, so we normally return MayAlias in that case. if (lhsSrc.origin == rhsSrc.origin) { LLVM_DEBUG(llvm::dbgs() << " aliasing because same source kind and origin\n"); @@ -125,6 +162,31 @@ AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) { return AliasResult::MayAlias; return AliasResult::MustAlias; } + // If one value is the address of a composite, and if the other value is the + // address of a pointer/allocatable component of that composite, their + // origins compare unequal because the latter has !isData(). As for the + // address of any component vs. the address of the composite, a store to one + // can affect a load from the other, so the result should be MayAlias. To + // catch this case, we conservatively return MayAlias when one value is the + // address of a composite, the other value is non-data, and they have the + // same origin value. + // + // TODO: That logic does not check that the latter is actually a component + // of the former, so it can return MayAlias when unnecessary. For example, + // they might both be addresses of components of a larger composite. + // + // FIXME: Actually, we should generalize from isRecordWithPointerComponent + // to any composite because a component with !isData() is not always a + // pointer. However, Source::isRecordWithPointerComponent currently doesn't + // actually check for pointer components, so it's fine for now. + if (lhsSrc.origin.u == rhsSrc.origin.u && + ((isRecordWithPointerComponent(lhs.getType()) && !rhsSrc.isData()) || + (isRecordWithPointerComponent(rhs.getType()) && !lhsSrc.isData()))) { + LLVM_DEBUG(llvm::dbgs() + << " aliasing between composite and non-data component with " + << "same source kind and origin value\n"); + return AliasResult::MayAlias; + } // Two host associated accesses may overlap due to an equivalence. if (lhsSrc.kind == SourceKind::HostAssoc) { @@ -134,12 +196,17 @@ AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) { } Source *src1, *src2; + mlir::Value *val1, *val2; if (lhsSrc.kind < rhsSrc.kind) { src1 = &lhsSrc; src2 = &rhsSrc; + val1 = &lhs; + val2 = &rhs; } else { src1 = &rhsSrc; src2 = &lhsSrc; + val1 = &rhs; + val2 = &lhs; } if (src1->kind == SourceKind::Argument && @@ -162,23 +229,88 @@ AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) { src2->attributes.set(Attribute::Target); } - // Dummy TARGET/POINTER argument may alias with a global TARGET/POINTER. + // Two TARGET/POINTERs may alias. The logic here focuses on data. Handling + // of non-data is included below. if (src1->isTargetOrPointer() && src2->isTargetOrPointer() && - src1->isData() == src2->isData()) { + src1->isData() && src2->isData()) { LLVM_DEBUG(llvm::dbgs() << " aliasing because of target or pointer\n"); return AliasResult::MayAlias; } - // Box for POINTER component inside an object of a derived type - // may alias box of a POINTER object, as well as boxes for POINTER - // components inside two objects of derived types may alias. - if ((isRecordWithPointerComponent(src1->valueType) && - src2->isTargetOrPointer()) || - (isRecordWithPointerComponent(src2->valueType) && - src1->isTargetOrPointer()) || - (isRecordWithPointerComponent(src1->valueType) && - isRecordWithPointerComponent(src2->valueType))) { - LLVM_DEBUG(llvm::dbgs() << " aliasing because of pointer components\n"); + // Aliasing for dummy arg with target attribute. + // + // The address of a dummy arg (or HostAssoc) may alias the address of a + // non-local (global or another dummy arg) when both have target attributes. + // If either is a composite, addresses of components may alias as well. + // + // The previous "if" calling isTargetOrPointer casts a very wide net and so + // reports MayAlias for many such cases that would otherwise be reported here. + // It specifically skips such cases where one or both values have !isData() + // (e.g., address *of* pointer/allocatable component vs. address of + // composite), so this "if" catches those cases. + if (src1->attributes.test(Attribute::Target) && + src2->attributes.test(Attribute::Target) && + ((src1->mayBeDummyArgOrHostAssoc() && src2->mayBeActualArg()) || + (src2->mayBeDummyArgOrHostAssoc() && src1->mayBeActualArg()))) { + LLVM_DEBUG(llvm::dbgs() + << " aliasing between targets where one is a dummy arg\n"); + return AliasResult::MayAlias; + } + + // Aliasing for dummy arg that is a pointer. + // + // The address of a pointer dummy arg (but not a pointer component of a dummy + // arg) may alias the address of either (1) a non-local pointer or (2) thus a + // non-local composite with a pointer component. A non-local might be a + // global or another dummy arg. The following is an example of the global + // composite case: + // + // module m + // type t + // real, pointer :: p + // end type + // type(t) :: a + // type(t) :: b + // contains + // subroutine test(p) + // real, pointer :: p + // p = 42 + // a = b + // print *, p + // end subroutine + // end module + // program main + // use m + // real, target :: x1 = 1 + // real, target :: x2 = 2 + // a%p => x1 + // b%p => x2 + // call test(a%p) + // end + // + // The dummy argument p is an alias for a%p, even for the purposes of pointer + // association during the assignment a = b. Thus, the program should print 2. + // + // The same is true when p is HostAssoc. For example, we might replace the + // test subroutine above with: + // + // subroutine test(p) + // real, pointer :: p + // call internal() + // contains + // subroutine internal() + // p = 42 + // a = b + // print *, p + // end subroutine + // end subroutine + if ((src1->mayBePtrDummyArgOrHostAssoc() && + src2->mayBeActualArgWithPtr(val2)) || + (src2->mayBePtrDummyArgOrHostAssoc() && + src1->mayBeActualArgWithPtr(val1))) { + LLVM_DEBUG(llvm::dbgs() + << " aliasing between pointer dummy arg and either pointer or " + << "composite with pointer component\n"); return AliasResult::MayAlias; } @@ -274,6 +406,8 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, followBoxData = true; }) .Case([&](auto op) { + if (isPointerReference(ty)) + attributes.set(Attribute::Pointer); v = op->getOperand(0); defOp = v.getDefiningOp(); if (mlir::isa(v.getType())) @@ -392,6 +526,8 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v, defOp = v.getDefiningOp(); }) .Case([&](auto op) { + auto varIf = llvm::cast(defOp); + attributes |= getAttrsFromVariable(varIf); // Track further through the memory indexed into // => if the source arrays/structures don't alias then nor do the // results of hlfir.designate diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir index 91829a461dc72..eab438576c2bc 100644 --- a/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir +++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir @@ -20,8 +20,8 @@ // CHECK-LABEL: Testing : "_QMmPtest // CHECK: a#0 <-> func.region0#0: MayAlias -// FIXME: a's box cannot alias with raw reference to f32 (x), so MayAlias below must be NoAlias: -// CHECK: a#0 <-> func.region0#1: MayAlias +// a's box cannot alias with raw reference to f32 (x) +// CHECK: a#0 <-> func.region0#1: NoAlias // pointer_dummy's box cannot alias with raw reference to f32 (x) // CHECK: func.region0#0 <-> func.region0#1: NoAlias @@ -46,7 +46,7 @@ func.func private @_QPtest2(!fir.ref) // ----- -// A composite with a pointer component may alias with a dummy +// A composite with a pointer component does not alias with a dummy // argument of composite type with a pointer component: // module m // type t @@ -63,7 +63,7 @@ func.func private @_QPtest2(!fir.ref) // end module m // CHECK-LABEL: Testing : "_QMmPtest" -// CHECK: a#0 <-> func.region0#0: MayAlias +// CHECK: a#0 <-> func.region0#0: NoAlias fir.global @_QMmEa : !fir.type<_QMmTt{pointer_component:!fir.box>}> { %0 = fir.undefined !fir.type<_QMmTt{pointer_component:!fir.box>}> @@ -88,7 +88,7 @@ func.func private @_QPtest2(!fir.ref) // ----- // Two dummy arguments of composite type with a pointer component -// may alias each other: +// do not alias each other: // module m // type t // real, pointer :: pointer_component @@ -103,7 +103,7 @@ func.func private @_QPtest2(!fir.ref) // end module m // CHECK-LABEL: Testing : "_QMmPtest" -// CHECK: func.region0#0 <-> func.region0#1: MayAlias +// CHECK: func.region0#0 <-> func.region0#1: NoAlias func.func @_QMmPtest(%arg0: !fir.ref>}>> {fir.bindc_name = "a"}, %arg1: !fir.ref>}>> {fir.bindc_name = "b"}, %arg2: !fir.ref {fir.bindc_name = "x", fir.target}) attributes {test.ptr = "func"} { %0 = fir.field_index pointer_component, !fir.type<_QMmTt{pointer_component:!fir.box>}> @@ -137,8 +137,7 @@ func.func private @_QPtest2(!fir.ref) // end module m // CHECK-LABEL: Testing : "_QMmPtest" -// FIXME: MayAlias must be NoAlias -// CHECK: func.region0#0 <-> func.region0#1: MayAlias +// CHECK: func.region0#0 <-> func.region0#1: NoAlias func.func @_QMmPtest(%arg0: !fir.ref>}>> {fir.bindc_name = "a"}, %arg1: !fir.ref>}>> {fir.bindc_name = "b"}) attributes {test.ptr = "func"} { %0 = fir.field_index allocatable_component, !fir.type<_QMmTt{allocatable_component:!fir.box>}> diff --git a/flang/test/Analysis/AliasAnalysis/ptr-component.fir b/flang/test/Analysis/AliasAnalysis/ptr-component.fir index ae53113f15d33..279143a581460 100644 --- a/flang/test/Analysis/AliasAnalysis/ptr-component.fir +++ b/flang/test/Analysis/AliasAnalysis/ptr-component.fir @@ -1,6 +1,13 @@ -// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file 2>&1 | FileCheck %s +// Check AliasAnalysis for pointer components. +// +// Throughout this test, The ".fir" suffix on symbols indicates a version of the +// MLIR after convert-hlfir-to-fir. A key difference is that component access +// is via fir.coordinate_of instead of hlfir.designate. We would like alias +// analysis results to be the same in both versions. -// ----- +// 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 // module m // type t @@ -18,23 +25,32 @@ // end module // CHECK-LABEL: Testing : "_QMmPfoo" -// TODO: x and y are non pointer, non target argument and therefore do not alias. -// CHECK-DAG: x#0 <-> y#0: MayAlias +// x and y are non pointer, non target argument and therefore do not alias. +// CHECK-DAG: x#0 <-> y#0: NoAlias +// CHECK-DAG: x.fir#0 <-> y.fir#0: NoAlias -// TODO: y is not a pointer object and therefore does not alias with the x%next component. -// Also assigning x to y would not modify x.next -// CHECK-DAG: y#0 <-> xnext1#0: MayAlias -// CHECK-DAG: y#0 <-> xnext2#0: MayAlias +// y is not a pointer object and therefore does not alias with the x%next +// component. Also assigning x to y would not modify x.next +// CHECK-DAG: y#0 <-> xnext1#0: NoAlias +// CHECK-DAG: y#0 <-> xnext2#0: NoAlias +// CHECK-DAG: y.fir#0 <-> xnext1.fir#0: NoAlias +// CHECK-DAG: y.fir#0 <-> xnext2.fir#0: NoAlias // We need to catch the fact that assigning y to x will modify xnext. // The only side-effect between the 2 loads of x.next is the assignment to x, // therefore x needs to alias with x.next to prevent the loads from being merged. // CHECK-DAG: x#0 <-> xnext1#0: MayAlias // CHECK-DAG: x#0 <-> xnext2#0: MayAlias +// CHECK-DAG: x.fir#0 <-> xnext1.fir#0: MayAlias +// CHECK-DAG: x.fir#0 <-> xnext2.fir#0: MayAlias // TODO: xnext1#0 <-> xnext2#0 are the same and therefore MustAlias but // we are currently not comparing operands involved in offset computations // CHECK-DAG: xnext1#0 <-> xnext2#0: MayAlias +// CHECK-DAG: xnext1.fir#0 <-> xnext2.fir#0: MayAlias + +// CHECK-DAG: xnext1#0 <-> ynext#0: NoAlias +// CHECK-DAG: xnext1.fir#0 <-> ynext.fir#0: NoAlias func.func @_QMmPfoo(%arg0: !fir.ref>>,i:i32}>> {fir.bindc_name = "x"}, %arg1: !fir.ref>>,i:i32}>> {fir.bindc_name = "y"}) { %0 = fir.alloca i32 {bindc_name = "i1", uniq_name = "_QMmFfooEi1"} @@ -56,6 +72,46 @@ func.func @_QMmPfoo(%arg0: !fir.ref>>,i:i32}>>) -> !fir.ref %15 = fir.load %14 : !fir.ref hlfir.assign %15 to %3#0 : i32, !fir.ref + %16 = hlfir.designate %5#0{"next"} {fortran_attrs = #fir.var_attrs, test.ptr = "ynext"} : (!fir.ref>>,i:i32}>>) -> !fir.ref>>,i:i32}>>>> + return +} + +func.func @_QMmPfoo.fir(%arg0: !fir.ref>>,i:i32}>> {fir.bindc_name = "x"}, %arg1: !fir.ref>>,i:i32}>> {fir.bindc_name = "y"}) { + %0 = fir.alloca !fir.box>>,i:i32}>> + %1 = fir.alloca i32 {bindc_name = "i1", uniq_name = "_QMmFfooEi1"} + %2 = fir.declare %1 {uniq_name = "_QMmFfooEi1"} : (!fir.ref) -> !fir.ref + %3 = fir.alloca i32 {bindc_name = "i2", uniq_name = "_QMmFfooEi2"} + %4 = fir.declare %3 {uniq_name = "_QMmFfooEi2"} : (!fir.ref) -> !fir.ref + %5 = fir.declare %arg0 {test.ptr = "x.fir", uniq_name = "_QMmFfooEx"} : (!fir.ref>>,i:i32}>>) -> !fir.ref>>,i:i32}>> + %6 = fir.declare %arg1 {test.ptr = "y.fir", uniq_name = "_QMmFfooEy"} : (!fir.ref>>,i:i32}>>) -> !fir.ref>>,i:i32}>> + %7 = fir.field_index next, !fir.type<_QMmTt{next:!fir.box>>,i:i32}> + %8 = fir.coordinate_of %5, %7 {test.ptr="xnext1.fir"} : (!fir.ref>>,i:i32}>>, !fir.field) -> !fir.ref>>,i:i32}>>>> + %9 = fir.load %8 : !fir.ref>>,i:i32}>>>> + %10 = fir.box_addr %9 : (!fir.box>>,i:i32}>>>) -> !fir.ptr>>,i:i32}>> + %11 = fir.field_index i, !fir.type<_QMmTt{next:!fir.box>>,i:i32}> + %12 = fir.coordinate_of %10, %11 : (!fir.ptr>>,i:i32}>>, !fir.field) -> !fir.ref + %13 = fir.load %12 : !fir.ref + fir.store %13 to %2 : !fir.ref + %14 = fir.embox %5 : (!fir.ref>>,i:i32}>>) -> !fir.box>>,i:i32}>> + %15 = fir.embox %6 : (!fir.ref>>,i:i32}>>) -> !fir.box>>,i:i32}>> + fir.store %14 to %0 : !fir.ref>>,i:i32}>>> + %16 = fir.address_of(@_QQclX746D70332E66697200) : !fir.ref> + %c9 = arith.constant 9 : index + %c14_i32 = arith.constant 14 : i32 + %17 = fir.convert %0 : (!fir.ref>>,i:i32}>>>) -> !fir.ref> + %18 = fir.convert %15 : (!fir.box>>,i:i32}>>) -> !fir.box + %19 = fir.convert %16 : (!fir.ref>) -> !fir.ref + %20 = fir.call @_FortranAAssign(%17, %18, %19, %c14_i32) : (!fir.ref>, !fir.box, !fir.ref, i32) -> none + %21 = fir.field_index next, !fir.type<_QMmTt{next:!fir.box>>,i:i32}> + %22 = fir.coordinate_of %5, %21 {test.ptr="xnext2.fir"}: (!fir.ref>>,i:i32}>>, !fir.field) -> !fir.ref>>,i:i32}>>>> + %23 = fir.load %22 : !fir.ref>>,i:i32}>>>> + %24 = fir.box_addr %23 : (!fir.box>>,i:i32}>>>) -> !fir.ptr>>,i:i32}>> + %25 = fir.field_index i, !fir.type<_QMmTt{next:!fir.box>>,i:i32}> + %26 = fir.coordinate_of %24, %25 : (!fir.ptr>>,i:i32}>>, !fir.field) -> !fir.ref + %27 = fir.load %26 : !fir.ref + fir.store %27 to %4 : !fir.ref + %28 = fir.field_index next, !fir.type<_QMmTt{next:!fir.box>>,i:i32}> + %29 = fir.coordinate_of %6, %28 {test.ptr="ynext.fir"} : (!fir.ref>>,i:i32}>>, !fir.field) -> !fir.ref>>,i:i32}>>>> return } @@ -64,6 +120,7 @@ func.func @_QMmPfoo(%arg0: !fir.ref ynext#0: MayAlias +// CHECK-DAG: xnext.fir#0 <-> ynext.fir#0: MayAlias func.func @_QMmPfoo2(%arg0: !fir.ref>>,i:i32}>> {fir.bindc_name = "x"}, %arg1: !fir.ref>>,i:i32}>> {fir.bindc_name = "y"}) { %4:2 = hlfir.declare %arg0 {uniq_name = "_QMmFfooEx"} : (!fir.ref>>,i:i32}>>) -> (!fir.ref>>,i:i32}>>, !fir.ref>>,i:i32}>>) @@ -78,6 +135,20 @@ func.func @_QMmPfoo2(%arg0: !fir.ref>>,i:i32}>> {fir.bindc_name = "x"}, %arg1: !fir.ref>>,i:i32}>> {fir.bindc_name = "y"}) { + %0 = fir.declare %arg0 {uniq_name = "_QMmFfooEx"} : (!fir.ref>>,i:i32}>>) -> !fir.ref>>,i:i32}>> + %1 = fir.declare %arg1 {uniq_name = "_QMmFfooEy"} : (!fir.ref>>,i:i32}>>) -> !fir.ref>>,i:i32}>> + %2 = fir.field_index next, !fir.type<_QMmTt{next:!fir.box>>,i:i32}> + %3 = fir.coordinate_of %0, %2 : (!fir.ref>>,i:i32}>>, !fir.field) -> !fir.ref>>,i:i32}>>>> + %4 = fir.load %3 : !fir.ref>>,i:i32}>>>> + %5 = fir.box_addr %4 {test.ptr = "xnext.fir"} : (!fir.box>>,i:i32}>>>) -> !fir.ptr>>,i:i32}>> + %6 = fir.field_index next, !fir.type<_QMmTt{next:!fir.box>>,i:i32}> + %7 = fir.coordinate_of %1, %6 : (!fir.ref>>,i:i32}>>, !fir.field) -> !fir.ref>>,i:i32}>>>> + %8 = fir.load %7 : !fir.ref>>,i:i32}>>>> + %9 = fir.box_addr %8 {test.ptr = "ynext.fir"} : (!fir.box>>,i:i32}>>>) -> !fir.ptr>>,i:i32}>> + return +} + // ----- // module m @@ -93,6 +164,7 @@ func.func @_QMmPfoo2(%arg0: !fir.ref xarray#0: MayAlias +// CHECK-DAG: yarray.fir#0 <-> xarray.fir#0: MayAlias func.func @_QMmPfoo3(%arg0: !fir.ref>>}>> {fir.bindc_name = "x"}, %arg1: !fir.ref>>}>> {fir.bindc_name = "y"}) { %0:2 = hlfir.declare %arg0 {uniq_name = "_QMmFfooEx"} : (!fir.ref>>}>>) -> (!fir.ref>>}>>, !fir.ref>>}>>) @@ -109,3 +181,659 @@ func.func @_QMmPfoo3(%arg0: !fir.ref return } + +func.func @_QMmPfoo3.fir(%arg0: !fir.ref>>}>> {fir.bindc_name = "x"}, %arg1: !fir.ref>>}>> {fir.bindc_name = "y"}) { + %0 = fir.declare %arg0 {uniq_name = "_QMmFfooEx"} : (!fir.ref>>}>>) -> !fir.ref>>}>> + %1 = fir.declare %arg1 {uniq_name = "_QMmFfooEy"} : (!fir.ref>>}>>) -> !fir.ref>>}>> + %2 = fir.field_index array, !fir.type<_QMmTta{array:!fir.box>>}> + %3 = fir.coordinate_of %1, %2 : (!fir.ref>>}>>, !fir.field) -> !fir.ref>>> + %4 = fir.load %3 : !fir.ref>>> + %c1 = arith.constant 1 : index + %c0 = arith.constant 0 : index + %5:3 = fir.box_dims %4, %c0 : (!fir.box>>, index) -> (index, index, index) + %6 = fir.shift %5#0 : (index) -> !fir.shift<1> + %7 = fir.array_coor %4(%6) %c1 {test.ptr="yarray.fir"} : (!fir.box>>, !fir.shift<1>, index) -> !fir.ref + %8 = fir.load %7 : !fir.ref + %9 = fir.field_index array, !fir.type<_QMmTta{array:!fir.box>>}> + %10 = fir.coordinate_of %0, %9 : (!fir.ref>>}>>, !fir.field) -> !fir.ref>>> + %11 = fir.load %10 : !fir.ref>>> + %c1_0 = arith.constant 1 : index + %c0_1 = arith.constant 0 : index + %12:3 = fir.box_dims %11, %c0_1 : (!fir.box>>, index) -> (index, index, index) + %13 = fir.shift %12#0 : (index) -> !fir.shift<1> + %14 = fir.array_coor %11(%13) %c1_0 {test.ptr="xarray.fir"} : (!fir.box>>, !fir.shift<1>, index) -> !fir.ref + fir.store %8 to %14 : !fir.ref + return +} + +// ----- + +// The address of a composite aliases the address of any component, including an +// allocatable component. Like the address of a pointer, the address of an +// allocatable is considered non-data, so AliasAnalysis has special handling to +// detect the aliasing. + +// module m +// type t +// integer, allocatable :: p +// end type +// type(t) :: x +// contains +// subroutine test() +// ! access x%p +// end subroutine +// end module + +// CHECK-LABEL: Testing : "_QMmPtest" +// CHECK-DAG: x#0 <-> x%p#0: MayAlias +// CHECK-DAG: x.fir#0 <-> x%p.fir#0: MayAlias + +func.func @_QMmPtest() { + %0 = fir.address_of(@_QMmEx) : !fir.ref>}>> + %1:2 = hlfir.declare %0 {test.ptr="x", uniq_name = "_QMmEx"} : (!fir.ref>}>>) -> (!fir.ref>}>>, !fir.ref>}>>) + %2 = hlfir.designate %1#0{"p"} {test.ptr="x%p", fortran_attrs = #fir.var_attrs} : (!fir.ref>}>>) -> !fir.ref>> + return +} + +func.func @_QMmPtest.fir() { + %0 = fir.address_of(@_QMmEx) : !fir.ref>}>> + %1 = fir.declare %0 {test.ptr = "x.fir", uniq_name = "_QMmEx"} : (!fir.ref>}>>) -> !fir.ref>}>> + %2 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box>}> + %3 = fir.coordinate_of %1, %2 {test.ptr="x%p.fir"} : (!fir.ref>}>>, !fir.field) -> !fir.ref>> + return +} + +// ----- + +// Nested composites. + +// module m +// type t1 +// integer, pointer :: p +// end type +// type t2 +// type(t1) :: x +// integer, pointer :: p +// integer :: i +// end type +// contains +// subroutine test() +// type(t2) :: x +// end subroutine +// end module + +// CHECK-LABEL: Testing : "_QMmPtest" + +// The addresses of a composite and its pointer component alias even if the +// composite is nested within another composite. +// CHECK-DAG: x#0 <-> x%p#0: MayAlias +// CHECK-DAG: x%x#0 <-> x%x%p#0: MayAlias +// CHECK-DAG: x.fir#0 <-> x%p.fir#0: MayAlias +// CHECK-DAG: x%x.fir#0 <-> x%x%p.fir#0: MayAlias + +// The addresses of different components of the same composite do not alias. +// +// TODO: Thus, all results below should be NoAlias. However, AliasAnalysis +// normally does not recognize when two values (x%x vs. x%i) are distinct +// components of the same composite (x) as opposed to being potentially +// overlapping parts of something, so it returns MayAlias. There is special +// handling for a pointer component (x%p) that does recognize it as separate +// from other components (x%i). But it does not yet distinguish the composite +// (x) from a component (x%x) that is also a composite with a pointer component +// (x%x%p). Thus, because x and x%p can alias, it assumes x%x and x%p can too. +// CHECK-DAG: x%x#0 <-> x%i#0: MayAlias +// CHECK-DAG: x%p#0 <-> x%i#0: NoAlias +// CHECK-DAG: x%x#0 <-> x%p#0: MayAlias +// CHECK-DAG: x%x.fir#0 <-> x%i.fir#0: MayAlias +// CHECK-DAG: x%p.fir#0 <-> x%i.fir#0: NoAlias +// CHECK-DAG: x%x.fir#0 <-> x%p.fir#0: MayAlias + +func.func @_QMmPtest() { + %0 = fir.alloca !fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box>}>,p:!fir.box>,i:i32}> {bindc_name = "x", uniq_name = "_QMmFtestEx"} + %1:2 = hlfir.declare %0 {test.ptr="x", uniq_name = "_QMmFtestEx"} : (!fir.ref>}>,p:!fir.box>,i:i32}>>) -> (!fir.ref>}>,p:!fir.box>,i:i32}>>, !fir.ref>}>,p:!fir.box>,i:i32}>>) + %2 = hlfir.designate %1#0{"x"} {test.ptr="x%x"} : (!fir.ref>}>,p:!fir.box>,i:i32}>>) -> !fir.ref>}>> + %3 = hlfir.designate %1#0{"p"} {test.ptr="x%p", fortran_attrs = #fir.var_attrs} : (!fir.ref>}>,p:!fir.box>,i:i32}>>) -> !fir.ref>> + %4 = hlfir.designate %1#0{"i"} {test.ptr="x%i"} : (!fir.ref>}>,p:!fir.box>,i:i32}>>) -> !fir.ref + %5 = hlfir.designate %2{"p"} {test.ptr="x%x%p", fortran_attrs = #fir.var_attrs} : (!fir.ref>}>>) -> !fir.ref>> + return +} + +func.func @_QMmPtest.fir() { + %0 = fir.alloca !fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box>}>,p:!fir.box>,i:i32}> {bindc_name = "x", uniq_name = "_QMmFtestEx"} + %1 = fir.declare %0 {test.ptr = "x.fir", uniq_name = "_QMmFtestEx"} : (!fir.ref>}>,p:!fir.box>,i:i32}>>) -> !fir.ref>}>,p:!fir.box>,i:i32}>> + %2 = fir.field_index x, !fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box>}>,p:!fir.box>,i:i32}> + %3 = fir.coordinate_of %1, %2 {test.ptr="x%x.fir"} : (!fir.ref>}>,p:!fir.box>,i:i32}>>, !fir.field) -> !fir.ref>}>> + %4 = fir.field_index p, !fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box>}>,p:!fir.box>,i:i32}> + %5 = fir.coordinate_of %1, %4 {test.ptr="x%p.fir"} : (!fir.ref>}>,p:!fir.box>,i:i32}>>, !fir.field) -> !fir.ref>> + %6 = fir.field_index i, !fir.type<_QMmTt2{x:!fir.type<_QMmTt1{p:!fir.box>}>,p:!fir.box>,i:i32}> + %7 = fir.coordinate_of %1, %6 {test.ptr="x%i.fir"} : (!fir.ref>}>,p:!fir.box>,i:i32}>>, !fir.field) -> !fir.ref + %8 = fir.field_index p, !fir.type<_QMmTt1{p:!fir.box>}> + %9 = fir.coordinate_of %3, %8 {test.ptr="x%x%p.fir"} : (!fir.ref>}>>, !fir.field) -> !fir.ref>> + return +} + +// ----- + +// Pointer that might dynamically be a component. + +// The address of a pointer dummy arg (argp) might alias the address of a +// pointer component (x%p) and thus the address of the associated composite (x), +// but it does not alias the addresses of other components (x%i) of the +// composite. Moreover, the address *in* argp does not alias any of those. +// Finally, an allocatable dummy arg (arga) should not be mistaken for a pointer +// dummy arg and cannot have such aliasing. + +// module m +// type t +// integer, pointer :: p +// integer i +// end type +// type(t) :: glob +// contains +// subroutine test(argp, arga, arg) +// integer, pointer :: argp +// integer, allocatable :: arga +// type(t) :: arg +// type(t) :: loc +// end subroutine +// end module + +// CHECK-LABEL: Testing : "_QMmPtest" + +// Check when composite is a dummy arg. +// +// CHECK-DAG: argp#0 <-> arg#0: MayAlias +// CHECK-DAG: argp#0 <-> arg%p#0: MayAlias +// CHECK-DAG: argp#0 <-> arg%i#0: NoAlias +// CHECK-DAG: argp.fir#0 <-> arg.fir#0: MayAlias +// CHECK-DAG: argp.fir#0 <-> arg%p.fir#0: MayAlias +// CHECK-DAG: argp.fir#0 <-> arg%i.fir#0: NoAlias +// +// CHECK-DAG: argp.tgt#0 <-> arg#0: NoAlias +// CHECK-DAG: argp.tgt#0 <-> arg%p#0: NoAlias +// CHECK-DAG: argp.tgt#0 <-> arg%i#0: NoAlias +// CHECK-DAG: argp.tgt.fir#0 <-> arg.fir#0: NoAlias +// CHECK-DAG: argp.tgt.fir#0 <-> arg%p.fir#0: NoAlias +// CHECK-DAG: argp.tgt.fir#0 <-> arg%i.fir#0: NoAlias +// +// CHECK-DAG: arga#0 <-> arg#0: NoAlias +// CHECK-DAG: arga#0 <-> arg%p#0: NoAlias +// CHECK-DAG: arga.fir#0 <-> arg.fir#0: NoAlias +// CHECK-DAG: arga.fir#0 <-> arg%p.fir#0: NoAlias + +// Check when composite is a global. +// +// CHECK-DAG: argp#0 <-> glob#0: MayAlias +// CHECK-DAG: argp#0 <-> glob%p#0: MayAlias +// CHECK-DAG: argp#0 <-> glob%i#0: NoAlias +// CHECK-DAG: argp.fir#0 <-> glob.fir#0: MayAlias +// CHECK-DAG: argp.fir#0 <-> glob%p.fir#0: MayAlias +// CHECK-DAG: argp.fir#0 <-> glob%i.fir#0: NoAlias +// +// CHECK-DAG: argp.tgt#0 <-> glob#0: NoAlias +// CHECK-DAG: argp.tgt#0 <-> glob%p#0: NoAlias +// CHECK-DAG: argp.tgt#0 <-> glob%i#0: NoAlias +// CHECK-DAG: argp.tgt.fir#0 <-> glob.fir#0: NoAlias +// CHECK-DAG: argp.tgt.fir#0 <-> glob%p.fir#0: NoAlias +// CHECK-DAG: argp.tgt.fir#0 <-> glob%i.fir#0: NoAlias +// +// CHECK-DAG: arga#0 <-> glob#0: NoAlias +// CHECK-DAG: arga#0 <-> glob%p#0: NoAlias +// CHECK-DAG: arga.fir#0 <-> glob.fir#0: NoAlias +// CHECK-DAG: arga.fir#0 <-> glob%p.fir#0: NoAlias + +// Check when composite is a local and thus cannot alias a dummy arg. +// +// CHECK-DAG: argp#0 <-> loc#0: NoAlias +// CHECK-DAG: argp#0 <-> loc%p#0: NoAlias +// CHECK-DAG: argp#0 <-> loc%i#0: NoAlias +// CHECK-DAG: argp.fir#0 <-> loc.fir#0: NoAlias +// CHECK-DAG: argp.fir#0 <-> loc%p.fir#0: NoAlias +// CHECK-DAG: argp.fir#0 <-> loc%i.fir#0: NoAlias +// +// CHECK-DAG: argp.tgt#0 <-> loc#0: NoAlias +// CHECK-DAG: argp.tgt#0 <-> loc%p#0: NoAlias +// CHECK-DAG: argp.tgt#0 <-> loc%i#0: NoAlias +// CHECK-DAG: argp.tgt.fir#0 <-> loc.fir#0: NoAlias +// CHECK-DAG: argp.tgt.fir#0 <-> loc%p.fir#0: NoAlias +// CHECK-DAG: argp.tgt.fir#0 <-> loc%i.fir#0: NoAlias +// +// CHECK-DAG: arga#0 <-> loc#0: NoAlias +// CHECK-DAG: arga#0 <-> loc%p#0: NoAlias +// CHECK-DAG: arga.fir#0 <-> loc.fir#0: NoAlias +// CHECK-DAG: arga.fir#0 <-> loc%p.fir#0: NoAlias + +fir.global @_QMmEglob : !fir.type<_QMmTt{p:!fir.box>,i:i32}> + +func.func @_QMmPtest(%arg0: !fir.ref>> {fir.bindc_name = "argp"}, %arg1: !fir.ref>> {fir.bindc_name = "arga"}, %arg2: !fir.ref>,i:i32}>> {fir.bindc_name = "arg"}) { + %0 = fir.dummy_scope : !fir.dscope + + %1:2 = hlfir.declare %arg0 dummy_scope %0 {test.ptr="argp", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmFtestEargp"} : (!fir.ref>>, !fir.dscope) -> (!fir.ref>>, !fir.ref>>) + %2 = fir.load %1#0 : !fir.ref>> + %3 = fir.box_addr %2 {test.ptr="argp.tgt"} : (!fir.box>) -> !fir.ptr + + %4:2 = hlfir.declare %arg1 dummy_scope %0 {test.ptr="arga", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmFtestEarga"} : (!fir.ref>>, !fir.dscope) -> (!fir.ref>>, !fir.ref>>) + + %5:2 = hlfir.declare %arg2 dummy_scope %0 {test.ptr="arg", uniq_name = "_QMmFtestEarg"} : (!fir.ref>,i:i32}>>, !fir.dscope) -> (!fir.ref>,i:i32}>>, !fir.ref>,i:i32}>>) + %6 = hlfir.designate %5#0{"p"} {test.ptr="arg%p", fortran_attrs = #fir.var_attrs} : (!fir.ref>,i:i32}>>) -> !fir.ref>> + %7 = hlfir.designate %5#0{"i"} {test.ptr="arg%i"} : (!fir.ref>,i:i32}>>) -> !fir.ref + + %8 = fir.address_of(@_QMmEglob) : !fir.ref>,i:i32}>> + %9:2 = hlfir.declare %8 {test.ptr="glob", uniq_name = "_QMmEglob"} : (!fir.ref>,i:i32}>>) -> (!fir.ref>,i:i32}>>, !fir.ref>,i:i32}>>) + %10 = hlfir.designate %9#0{"p"} {test.ptr="glob%p", fortran_attrs = #fir.var_attrs} : (!fir.ref>,i:i32}>>) -> !fir.ref>> + %11 = hlfir.designate %9#0{"i"} {test.ptr="glob%i"} : (!fir.ref>,i:i32}>>) -> !fir.ref + + %12 = fir.alloca !fir.type<_QMmTt{p:!fir.box>,i:i32}> {bindc_name = "loc", uniq_name = "_QMmFtestEloc"} + %13:2 = hlfir.declare %12 {test.ptr="loc", uniq_name = "_QMmFtestEloc"} : (!fir.ref>,i:i32}>>) -> (!fir.ref>,i:i32}>>, !fir.ref>,i:i32}>>) + %14 = hlfir.designate %13#0{"p"} {test.ptr="loc%p", fortran_attrs = #fir.var_attrs} : (!fir.ref>,i:i32}>>) -> !fir.ref>> + %15 = hlfir.designate %13#0{"i"} {test.ptr="loc%i"} : (!fir.ref>,i:i32}>>) -> !fir.ref + + return +} + +func.func @_QMmPtest.fir(%arg0: !fir.ref>> {fir.bindc_name = "argp"}, %arg1: !fir.ref>> {fir.bindc_name = "arga"}, %arg2: !fir.ref>,i:i32}>> {fir.bindc_name = "arg"}) { + %0 = fir.dummy_scope : !fir.dscope + + %1 = fir.declare %arg0 dummy_scope %0 {fortran_attrs = #fir.var_attrs, test.ptr = "argp.fir", uniq_name = "_QMmFtestEargp"} : (!fir.ref>>, !fir.dscope) -> !fir.ref>> + %2 = fir.load %1 : !fir.ref>> + %3 = fir.box_addr %2 {test.ptr = "argp.tgt.fir"} : (!fir.box>) -> !fir.ptr + + %4 = fir.declare %arg1 dummy_scope %0 {fortran_attrs = #fir.var_attrs, test.ptr = "arga.fir", uniq_name = "_QMmFtestEarga"} : (!fir.ref>>, !fir.dscope) -> !fir.ref>> + + %5 = fir.declare %arg2 dummy_scope %0 {test.ptr = "arg.fir", uniq_name = "_QMmFtestEarg"} : (!fir.ref>,i:i32}>>, !fir.dscope) -> !fir.ref>,i:i32}>> + %6 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box>,i:i32}> + %7 = fir.coordinate_of %5, %6 {test.ptr="arg%p.fir"} : (!fir.ref>,i:i32}>>, !fir.field) -> !fir.ref>> + %8 = fir.field_index i, !fir.type<_QMmTt{p:!fir.box>,i:i32}> + %9 = fir.coordinate_of %5, %8 {test.ptr="arg%i.fir"} : (!fir.ref>,i:i32}>>, !fir.field) -> !fir.ref + + %10 = fir.address_of(@_QMmEglob) : !fir.ref>,i:i32}>> + %11 = fir.declare %10 {test.ptr = "glob.fir", uniq_name = "_QMmEglob"} : (!fir.ref>,i:i32}>>) -> !fir.ref>,i:i32}>> + %12 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box>,i:i32}> + %13 = fir.coordinate_of %11, %12 {test.ptr="glob%p.fir"} : (!fir.ref>,i:i32}>>, !fir.field) -> !fir.ref>> + %14 = fir.field_index i, !fir.type<_QMmTt{p:!fir.box>,i:i32}> + %15 = fir.coordinate_of %11, %14 {test.ptr="glob%i.fir"} : (!fir.ref>,i:i32}>>, !fir.field) -> !fir.ref + + %16 = fir.alloca !fir.type<_QMmTt{p:!fir.box>,i:i32}> {bindc_name = "loc", uniq_name = "_QMmFtestEloc"} + %17 = fir.declare %16 {test.ptr = "loc.fir", uniq_name = "_QMmFtestEloc"} : (!fir.ref>,i:i32}>>) -> !fir.ref>,i:i32}>> + %18 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box>,i:i32}> + %19 = fir.coordinate_of %17, %18 {test.ptr="loc%p.fir"} : (!fir.ref>,i:i32}>>, !fir.field) -> !fir.ref>> + %20 = fir.field_index i, !fir.type<_QMmTt{p:!fir.box>,i:i32}> + %21 = fir.coordinate_of %17, %20 {test.ptr="loc%i.fir"} : (!fir.ref>,i:i32}>>, !fir.field) -> !fir.ref + + return +} + +// ----- + +// Same as previous case but argp and arga are HostAssoc. + +// module m +// type t +// integer, pointer :: p +// integer i +// end type +// type(t) :: glob +// contains +// subroutine parent(argp, arga) +// integer, pointer :: argp +// integer, allocatable :: arga +// type(t) :: arg +// call test(arg) +// contains +// subroutine test(arg) +// type(t) :: arg +// type(t) :: loc +// end subroutine +// end subroutine +// end module + +// CHECK-LABEL: Testing : "_QMmFparentPtest" + +// Check when composite is a dummy arg. +// +// CHECK-DAG: argp#0 <-> arg#0: MayAlias +// CHECK-DAG: argp#0 <-> arg%p#0: MayAlias +// CHECK-DAG: argp#0 <-> arg%i#0: NoAlias +// CHECK-DAG: argp.fir#0 <-> arg.fir#0: MayAlias +// CHECK-DAG: argp.fir#0 <-> arg%p.fir#0: MayAlias +// CHECK-DAG: argp.fir#0 <-> arg%i.fir#0: NoAlias +// +// TODO: Shouldn't these be NoAlias? However, argp.tgt is currently handled as +// Indirect. +// CHECK-DAG: argp.tgt#0 <-> arg#0: MayAlias +// CHECK-DAG: argp.tgt#0 <-> arg%p#0: MayAlias +// CHECK-DAG: argp.tgt#0 <-> arg%i#0: MayAlias +// CHECK-DAG: argp.tgt.fir#0 <-> arg.fir#0: MayAlias +// CHECK-DAG: argp.tgt.fir#0 <-> arg%p.fir#0: MayAlias +// CHECK-DAG: argp.tgt.fir#0 <-> arg%i.fir#0: MayAlias +// +// CHECK-DAG: arga#0 <-> arg#0: NoAlias +// CHECK-DAG: arga#0 <-> arg%p#0: NoAlias +// CHECK-DAG: arga.fir#0 <-> arg.fir#0: NoAlias +// CHECK-DAG: arga.fir#0 <-> arg%p.fir#0: NoAlias + +// Check when composite is a global. +// +// CHECK-DAG: argp#0 <-> glob#0: MayAlias +// CHECK-DAG: argp#0 <-> glob%p#0: MayAlias +// CHECK-DAG: argp#0 <-> glob%i#0: NoAlias +// CHECK-DAG: argp.fir#0 <-> glob.fir#0: MayAlias +// CHECK-DAG: argp.fir#0 <-> glob%p.fir#0: MayAlias +// CHECK-DAG: argp.fir#0 <-> glob%i.fir#0: NoAlias +// +// TODO: Shouldn't these be NoAlias? However, argp.tgt is currently handled as +// Indirect. +// CHECK-DAG: argp.tgt#0 <-> glob#0: MayAlias +// CHECK-DAG: argp.tgt#0 <-> glob%p#0: MayAlias +// CHECK-DAG: argp.tgt#0 <-> glob%i#0: MayAlias +// CHECK-DAG: argp.tgt.fir#0 <-> glob.fir#0: MayAlias +// CHECK-DAG: argp.tgt.fir#0 <-> glob%p.fir#0: MayAlias +// CHECK-DAG: argp.tgt.fir#0 <-> glob%i.fir#0: MayAlias +// +// CHECK-DAG: arga#0 <-> glob#0: NoAlias +// CHECK-DAG: arga#0 <-> glob%p#0: NoAlias +// CHECK-DAG: arga.fir#0 <-> glob.fir#0: NoAlias +// CHECK-DAG: arga.fir#0 <-> glob%p.fir#0: NoAlias + +// Check when composite is a local and thus cannot alias a dummy arg. +// +// CHECK-DAG: argp#0 <-> loc#0: NoAlias +// CHECK-DAG: argp#0 <-> loc%p#0: NoAlias +// CHECK-DAG: argp#0 <-> loc%i#0: NoAlias +// CHECK-DAG: argp.fir#0 <-> loc.fir#0: NoAlias +// CHECK-DAG: argp.fir#0 <-> loc%p.fir#0: NoAlias +// CHECK-DAG: argp.fir#0 <-> loc%i.fir#0: NoAlias +// +// TODO: Shouldn't these be NoAlias? However, argp.tgt is currently handled as +// Indirect. +// CHECK-DAG: argp.tgt#0 <-> loc#0: MayAlias +// CHECK-DAG: argp.tgt#0 <-> loc%p#0: MayAlias +// CHECK-DAG: argp.tgt#0 <-> loc%i#0: MayAlias +// CHECK-DAG: argp.tgt.fir#0 <-> loc.fir#0: MayAlias +// CHECK-DAG: argp.tgt.fir#0 <-> loc%p.fir#0: MayAlias +// CHECK-DAG: argp.tgt.fir#0 <-> loc%i.fir#0: MayAlias +// +// CHECK-DAG: arga#0 <-> loc#0: NoAlias +// CHECK-DAG: arga#0 <-> loc%p#0: NoAlias +// CHECK-DAG: arga.fir#0 <-> loc.fir#0: NoAlias +// CHECK-DAG: arga.fir#0 <-> loc%p.fir#0: NoAlias + +fir.global @_QMmEglob : !fir.type<_QMmTt{p:!fir.box>,i:i32}> + +func.func private @_QMmFparentPtest(%arg0: !fir.ref>,i:i32}>> {fir.bindc_name = "arg"}, %arg1: !fir.ref>>, !fir.ref>>>> {fir.host_assoc}) attributes {fir.host_symbol = @_QMmPparent, llvm.linkage = #llvm.linkage} { + %0 = fir.dummy_scope : !fir.dscope + + %c0_i32 = arith.constant 0 : i32 + %1 = fir.coordinate_of %arg1, %c0_i32 : (!fir.ref>>, !fir.ref>>>>, i32) -> !fir.llvm_ptr>>> + %2 = fir.load %1 : !fir.llvm_ptr>>> + %3:2 = hlfir.declare %2 {test.ptr="argp", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmFparentEargp"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) + %4 = fir.load %3#0 : !fir.ref>> + %5 = fir.box_addr %4 {test.ptr="argp.tgt"} : (!fir.box>) -> !fir.ptr + + %c1_i32 = arith.constant 1 : i32 + %10 = fir.coordinate_of %arg1, %c1_i32 : (!fir.ref>>, !fir.ref>>>>, i32) -> !fir.llvm_ptr>>> + %11 = fir.load %10 : !fir.llvm_ptr>>> + %12:2 = hlfir.declare %11 {test.ptr="arga", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmFparentEarga"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) + + %20 = fir.address_of(@_QMmEglob) : !fir.ref>,i:i32}>> + %21:2 = hlfir.declare %20 {test.ptr="glob", uniq_name = "_QMmEglob"} : (!fir.ref>,i:i32}>>) -> (!fir.ref>,i:i32}>>, !fir.ref>,i:i32}>>) + %22 = hlfir.designate %21#0{"p"} {test.ptr="glob%p", fortran_attrs = #fir.var_attrs} : (!fir.ref>,i:i32}>>) -> !fir.ref>> + %23 = hlfir.designate %21#0{"i"} {test.ptr="glob%i"} : (!fir.ref>,i:i32}>>) -> !fir.ref + + %30:2 = hlfir.declare %arg0 dummy_scope %0 {test.ptr="arg", uniq_name = "_QMmFparentFtestEarg"} : (!fir.ref>,i:i32}>>, !fir.dscope) -> (!fir.ref>,i:i32}>>, !fir.ref>,i:i32}>>) + %31 = hlfir.designate %30#0{"p"} {test.ptr="arg%p", fortran_attrs = #fir.var_attrs} : (!fir.ref>,i:i32}>>) -> !fir.ref>> + %32 = hlfir.designate %30#0{"i"} {test.ptr="arg%i"} : (!fir.ref>,i:i32}>>) -> !fir.ref + + %40 = fir.alloca !fir.type<_QMmTt{p:!fir.box>,i:i32}> {bindc_name = "loc", uniq_name = "_QMmFparentFtestEloc"} + %41:2 = hlfir.declare %40 {test.ptr="loc", uniq_name = "_QMmFparentFtestEloc"} : (!fir.ref>,i:i32}>>) -> (!fir.ref>,i:i32}>>, !fir.ref>,i:i32}>>) + %42 = hlfir.designate %41#0{"p"} {test.ptr="loc%p", fortran_attrs = #fir.var_attrs} : (!fir.ref>,i:i32}>>) -> !fir.ref>> + %43 = hlfir.designate %41#0{"i"} {test.ptr="loc%i"} : (!fir.ref>,i:i32}>>) -> !fir.ref + + return +} + +func.func private @_QMmFparentPtest.fir(%arg0: !fir.ref>,i:i32}>> {fir.bindc_name = "arg"}, %arg1: !fir.ref>>, !fir.ref>>>> {fir.host_assoc}) attributes {fir.host_symbol = @_QMmPparent, llvm.linkage = #llvm.linkage} { + %0 = fir.dummy_scope : !fir.dscope + + %c0_i32 = arith.constant 0 : i32 + %1 = fir.coordinate_of %arg1, %c0_i32 : (!fir.ref>>, !fir.ref>>>>, i32) -> !fir.llvm_ptr>>> + %2 = fir.load %1 : !fir.llvm_ptr>>> + %3 = fir.declare %2 {fortran_attrs = #fir.var_attrs, test.ptr = "argp.fir", uniq_name = "_QMmFparentEargp"} : (!fir.ref>>) -> !fir.ref>> + %4 = fir.load %3 : !fir.ref>> + %5 = fir.box_addr %4 {test.ptr = "argp.tgt.fir"} : (!fir.box>) -> !fir.ptr + + %c1_i32 = arith.constant 1 : i32 + %6 = fir.coordinate_of %arg1, %c1_i32 : (!fir.ref>>, !fir.ref>>>>, i32) -> !fir.llvm_ptr>>> + %7 = fir.load %6 : !fir.llvm_ptr>>> + %8 = fir.declare %7 {fortran_attrs = #fir.var_attrs, test.ptr = "arga.fir", uniq_name = "_QMmFparentEarga"} : (!fir.ref>>) -> !fir.ref>> + + %9 = fir.address_of(@_QMmEglob) : !fir.ref>,i:i32}>> + %10 = fir.declare %9 {test.ptr = "glob.fir", uniq_name = "_QMmEglob"} : (!fir.ref>,i:i32}>>) -> !fir.ref>,i:i32}>> + %11 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box>,i:i32}> + %12 = fir.coordinate_of %10, %11 {test.ptr="glob%p.fir"} : (!fir.ref>,i:i32}>>, !fir.field) -> !fir.ref>> + %13 = fir.field_index i, !fir.type<_QMmTt{p:!fir.box>,i:i32}> + %14 = fir.coordinate_of %10, %13 {test.ptr="glob%i.fir"} : (!fir.ref>,i:i32}>>, !fir.field) -> !fir.ref + + %15 = fir.declare %arg0 dummy_scope %0 {test.ptr = "arg.fir", uniq_name = "_QMmFparentFtestEarg"} : (!fir.ref>,i:i32}>>, !fir.dscope) -> !fir.ref>,i:i32}>> + %16 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box>,i:i32}> + %17 = fir.coordinate_of %15, %16 {test.ptr="arg%p.fir"} : (!fir.ref>,i:i32}>>, !fir.field) -> !fir.ref>> + %18 = fir.field_index i, !fir.type<_QMmTt{p:!fir.box>,i:i32}> + %19 = fir.coordinate_of %15, %18 {test.ptr="arg%i.fir"} : (!fir.ref>,i:i32}>>, !fir.field) -> !fir.ref + + %20 = fir.alloca !fir.type<_QMmTt{p:!fir.box>,i:i32}> {bindc_name = "loc", uniq_name = "_QMmFparentFtestEloc"} + %21 = fir.declare %20 {test.ptr = "loc.fir", uniq_name = "_QMmFparentFtestEloc"} : (!fir.ref>,i:i32}>>) -> !fir.ref>,i:i32}>> + %22 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box>,i:i32}> + %23 = fir.coordinate_of %21, %22 {test.ptr="loc%p.fir"} : (!fir.ref>,i:i32}>>, !fir.field) -> !fir.ref>> + %24 = fir.field_index i, !fir.type<_QMmTt{p:!fir.box>,i:i32}> + %25 = fir.coordinate_of %21, %24 {test.ptr="loc%i.fir"} : (!fir.ref>,i:i32}>>, !fir.field) -> !fir.ref + + return +} + +// ----- + +// Dummy arg with target attribute and pointer component. + +// The address of a dummy arg (arg0) may alias the address of a global (glob0) +// or another dummy arg (arg1) when both have target attributes. If either is a +// composite, the addresses of components (whether data like r, or non-data like +// p and a) may also alias the composite or the same component. However, target +// attributes do not permit two globals (glob0 and glob1) to alias. + +// module m +// type t +// real, pointer :: p +// real, allocatable :: a +// real :: r +// end type +// type(t), target :: glob0 +// type(t), target :: glob1 +// contains +// subroutine test(arg0, arg1) +// type(t), target :: arg0 +// type(t), target :: arg1 +// end subroutine +// end module + +// TODO: All glob0 vs. glob1 cases can be NoAlias. However, AliasAnalysis +// currently indiscriminately treats all targets that are data (addresses of +// glob[01] and glob[01]%r but not glob[01]%p and glob[01]%a) as aliasing. + +// Check composite vs. composite. +// +// CHECK-DAG: arg0#0 <-> arg1#0: MayAlias +// CHECK-DAG: arg0.fir#0 <-> arg1.fir#0: MayAlias +// +// CHECK-DAG: arg0#0 <-> glob0#0: MayAlias +// CHECK-DAG: arg0.fir#0 <-> glob0.fir#0: MayAlias +// +// CHECK-DAG: glob0#0 <-> glob1#0: MayAlias +// CHECK-DAG: glob0.fir#0 <-> glob1.fir#0: MayAlias + +// Check component vs. composite. +// +// CHECK-DAG: arg0%p#0 <-> arg1#0: MayAlias +// CHECK-DAG: arg0%a#0 <-> arg1#0: MayAlias +// CHECK-DAG: arg0%r#0 <-> arg1#0: MayAlias +// CHECK-DAG: arg0%p.fir#0 <-> arg1.fir#0: MayAlias +// CHECK-DAG: arg0%a.fir#0 <-> arg1.fir#0: MayAlias +// CHECK-DAG: arg0%r.fir#0 <-> arg1.fir#0: MayAlias +// +// CHECK-DAG: arg0%p#0 <-> glob0#0: MayAlias +// CHECK-DAG: arg0%a#0 <-> glob0#0: MayAlias +// CHECK-DAG: arg0%r#0 <-> glob0#0: MayAlias +// CHECK-DAG: arg0%p.fir#0 <-> glob0.fir#0: MayAlias +// CHECK-DAG: arg0%a.fir#0 <-> glob0.fir#0: MayAlias +// CHECK-DAG: arg0%r.fir#0 <-> glob0.fir#0: MayAlias +// +// CHECK-DAG: glob0%p#0 <-> glob1#0: NoAlias +// CHECK-DAG: glob0%a#0 <-> glob1#0: NoAlias +// CHECK-DAG: glob0%r#0 <-> glob1#0: MayAlias +// CHECK-DAG: glob0%p.fir#0 <-> glob1.fir#0: NoAlias +// CHECK-DAG: glob0%a.fir#0 <-> glob1.fir#0: NoAlias +// CHECK-DAG: glob0%r.fir#0 <-> glob1.fir#0: MayAlias + +// Check composite vs. component. +// +// CHECK-DAG: arg0#0 <-> arg1%p#0: MayAlias +// CHECK-DAG: arg0#0 <-> arg1%a#0: MayAlias +// CHECK-DAG: arg0#0 <-> arg1%r#0: MayAlias +// CHECK-DAG: arg0.fir#0 <-> arg1%p.fir#0: MayAlias +// CHECK-DAG: arg0.fir#0 <-> arg1%a.fir#0: MayAlias +// CHECK-DAG: arg0.fir#0 <-> arg1%r.fir#0: MayAlias +// +// CHECK-DAG: arg0#0 <-> glob0%p#0: MayAlias +// CHECK-DAG: arg0#0 <-> glob0%a#0: MayAlias +// CHECK-DAG: arg0#0 <-> glob0%r#0: MayAlias +// CHECK-DAG: arg0.fir#0 <-> glob0%p.fir#0: MayAlias +// CHECK-DAG: arg0.fir#0 <-> glob0%a.fir#0: MayAlias +// CHECK-DAG: arg0.fir#0 <-> glob0%r.fir#0: MayAlias +// +// CHECK-DAG: glob0#0 <-> glob1%p#0: NoAlias +// CHECK-DAG: glob0#0 <-> glob1%a#0: NoAlias +// CHECK-DAG: glob0#0 <-> glob1%r#0: MayAlias +// CHECK-DAG: glob0.fir#0 <-> glob1%p.fir#0: NoAlias +// CHECK-DAG: glob0.fir#0 <-> glob1%a.fir#0: NoAlias +// CHECK-DAG: glob0.fir#0 <-> glob1%r.fir#0: MayAlias + +// Check component vs. component. +// +// CHECK-DAG: arg0%p#0 <-> arg1%p#0: MayAlias +// CHECK-DAG: arg0%a#0 <-> arg1%a#0: MayAlias +// CHECK-DAG: arg0%r#0 <-> arg1%r#0: MayAlias +// CHECK-DAG: arg0%p.fir#0 <-> arg1%p.fir#0: MayAlias +// CHECK-DAG: arg0%a.fir#0 <-> arg1%a.fir#0: MayAlias +// CHECK-DAG: arg0%r.fir#0 <-> arg1%r.fir#0: MayAlias +// +// CHECK-DAG: arg0%p.fir#0 <-> glob0%p.fir#0: MayAlias +// CHECK-DAG: arg0%a.fir#0 <-> glob0%a.fir#0: MayAlias +// CHECK-DAG: arg0%r.fir#0 <-> glob0%r.fir#0: MayAlias +// +// CHECK-DAG: glob0%p.fir#0 <-> glob1%p.fir#0: NoAlias +// CHECK-DAG: glob0%a.fir#0 <-> glob1%a.fir#0: NoAlias +// CHECK-DAG: glob0%r.fir#0 <-> glob1%r.fir#0: MayAlias + +func.func @_QMmPtest(%arg0: !fir.ref>,a:!fir.box>,r:f32}>> {fir.bindc_name = "arg0", fir.target}, %arg1: !fir.ref>,a:!fir.box>,r:f32}>> {fir.bindc_name = "arg1", fir.target}) { + %0 = fir.dummy_scope : !fir.dscope + + %10:2 = hlfir.declare %arg0 dummy_scope %0 {test.ptr="arg0", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmFtestEarg0"} : (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.dscope) -> (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.ref>,a:!fir.box>,r:f32}>>) + %11 = hlfir.designate %10#0{"p"} {test.ptr="arg0%p", fortran_attrs = #fir.var_attrs} : (!fir.ref>,a:!fir.box>,r:f32}>>) -> !fir.ref>> + %12 = hlfir.designate %10#0{"a"} {test.ptr="arg0%a", fortran_attrs = #fir.var_attrs} : (!fir.ref>,a:!fir.box>,r:f32}>>) -> !fir.ref>> + %13 = hlfir.designate %10#0{"r"} {test.ptr="arg0%r"} : (!fir.ref>,a:!fir.box>,r:f32}>>) -> !fir.ref + + %20:2 = hlfir.declare %arg1 dummy_scope %0 {test.ptr="arg1", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmFtestEarg1"} : (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.dscope) -> (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.ref>,a:!fir.box>,r:f32}>>) + %21 = hlfir.designate %20#0{"p"} {test.ptr="arg1%p", fortran_attrs = #fir.var_attrs} : (!fir.ref>,a:!fir.box>,r:f32}>>) -> !fir.ref>> + %22 = hlfir.designate %20#0{"a"} {test.ptr="arg1%a", fortran_attrs = #fir.var_attrs} : (!fir.ref>,a:!fir.box>,r:f32}>>) -> !fir.ref>> + %23 = hlfir.designate %20#0{"r"} {test.ptr="arg1%r"} : (!fir.ref>,a:!fir.box>,r:f32}>>) -> !fir.ref + + %30 = fir.address_of(@_QMmEglob0) : !fir.ref>,a:!fir.box>,r:f32}>> + %31:2 = hlfir.declare %30 {test.ptr="glob0", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmEglob0"} : (!fir.ref>,a:!fir.box>,r:f32}>>) -> (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.ref>,a:!fir.box>,r:f32}>>) + %32 = hlfir.designate %31#0{"p"} {test.ptr="glob0%p", fortran_attrs = #fir.var_attrs} : (!fir.ref>,a:!fir.box>,r:f32}>>) -> !fir.ref>> + %33 = hlfir.designate %31#0{"a"} {test.ptr="glob0%a", fortran_attrs = #fir.var_attrs} : (!fir.ref>,a:!fir.box>,r:f32}>>) -> !fir.ref>> + %34 = hlfir.designate %31#0{"r"} {test.ptr="glob0%r"} : (!fir.ref>,a:!fir.box>,r:f32}>>) -> !fir.ref + + %40 = fir.address_of(@_QMmEglob1) : !fir.ref>,a:!fir.box>,r:f32}>> + %41:2 = hlfir.declare %40 {test.ptr="glob1", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmEglob1"} : (!fir.ref>,a:!fir.box>,r:f32}>>) -> (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.ref>,a:!fir.box>,r:f32}>>) + %42 = hlfir.designate %41#0{"p"} {test.ptr="glob1%p", fortran_attrs = #fir.var_attrs} : (!fir.ref>,a:!fir.box>,r:f32}>>) -> !fir.ref>> + %43 = hlfir.designate %41#0{"a"} {test.ptr="glob1%a", fortran_attrs = #fir.var_attrs} : (!fir.ref>,a:!fir.box>,r:f32}>>) -> !fir.ref>> + %44 = hlfir.designate %41#0{"r"} {test.ptr="glob1%r"} : (!fir.ref>,a:!fir.box>,r:f32}>>) -> !fir.ref + + return +} + +func.func @_QMmPtest.fir(%arg0: !fir.ref>,a:!fir.box>,r:f32}>> {fir.bindc_name = "arg0", fir.target}, %arg1: !fir.ref>,a:!fir.box>,r:f32}>> {fir.bindc_name = "arg1", fir.target}) { + %0 = fir.dummy_scope : !fir.dscope + + %1 = fir.declare %arg0 dummy_scope %0 {test.ptr="arg0.fir", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmFtestEarg0"} : (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.dscope) -> !fir.ref>,a:!fir.box>,r:f32}>> + %2 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box>,a:!fir.box>,r:f32}> + %3 = fir.coordinate_of %1, %2 {test.ptr="arg0%p.fir"} : (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.field) -> !fir.ref>> + %4 = fir.field_index a, !fir.type<_QMmTt{p:!fir.box>,a:!fir.box>,r:f32}> + %5 = fir.coordinate_of %1, %4 {test.ptr="arg0%a.fir"} : (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.field) -> !fir.ref>> + %6 = fir.field_index r, !fir.type<_QMmTt{p:!fir.box>,a:!fir.box>,r:f32}> + %7 = fir.coordinate_of %1, %6 {test.ptr="arg0%r.fir"} : (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.field) -> !fir.ref + + %8 = fir.declare %arg1 dummy_scope %0 {test.ptr="arg1.fir", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmFtestEarg1"} : (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.dscope) -> !fir.ref>,a:!fir.box>,r:f32}>> + %9 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box>,a:!fir.box>,r:f32}> + %10 = fir.coordinate_of %8, %9 {test.ptr="arg1%p.fir"} : (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.field) -> !fir.ref>> + %11 = fir.field_index a, !fir.type<_QMmTt{p:!fir.box>,a:!fir.box>,r:f32}> + %12 = fir.coordinate_of %8, %11 {test.ptr="arg1%a.fir"} : (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.field) -> !fir.ref>> + %13 = fir.field_index r, !fir.type<_QMmTt{p:!fir.box>,a:!fir.box>,r:f32}> + %14 = fir.coordinate_of %8, %13 {test.ptr="arg1%r.fir"} : (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.field) -> !fir.ref + + %15 = fir.address_of(@_QMmEglob0) : !fir.ref>,a:!fir.box>,r:f32}>> + %16 = fir.declare %15 {test.ptr="glob0.fir", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmEglob0"} : (!fir.ref>,a:!fir.box>,r:f32}>>) -> !fir.ref>,a:!fir.box>,r:f32}>> + %17 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box>,a:!fir.box>,r:f32}> + %18 = fir.coordinate_of %16, %17 {test.ptr="glob0%p.fir"} : (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.field) -> !fir.ref>> + %19 = fir.field_index a, !fir.type<_QMmTt{p:!fir.box>,a:!fir.box>,r:f32}> + %20 = fir.coordinate_of %16, %19 {test.ptr="glob0%a.fir"} : (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.field) -> !fir.ref>> + %21 = fir.field_index r, !fir.type<_QMmTt{p:!fir.box>,a:!fir.box>,r:f32}> + %22 = fir.coordinate_of %16, %21 {test.ptr="glob0%r.fir"} : (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.field) -> !fir.ref + + %23 = fir.address_of(@_QMmEglob1) : !fir.ref>,a:!fir.box>,r:f32}>> + %24 = fir.declare %23 {test.ptr="glob1.fir", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmEglob1"} : (!fir.ref>,a:!fir.box>,r:f32}>>) -> !fir.ref>,a:!fir.box>,r:f32}>> + %25 = fir.field_index p, !fir.type<_QMmTt{p:!fir.box>,a:!fir.box>,r:f32}> + %26 = fir.coordinate_of %24, %25 {test.ptr="glob1%p.fir"} : (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.field) -> !fir.ref>> + %27 = fir.field_index a, !fir.type<_QMmTt{p:!fir.box>,a:!fir.box>,r:f32}> + %28 = fir.coordinate_of %24, %27 {test.ptr="glob1%a.fir"} : (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.field) -> !fir.ref>> + %29 = fir.field_index r, !fir.type<_QMmTt{p:!fir.box>,a:!fir.box>,r:f32}> + %30 = fir.coordinate_of %24, %29 {test.ptr="glob1%r.fir"} : (!fir.ref>,a:!fir.box>,r:f32}>>, !fir.field) -> !fir.ref + + return +} + +// ----- + +// Allocatable dummy arg with target attribute. + +// This case is like the previous one except that the non-data is the address +// of the dummy arg itself rather than of a pointer component of the dummy arg. +// The goal is to check that logic introduced into AliasAnalysis to handle the +// pointer component case doesn't break this related one. + +// module m +// real, allocatable, target :: glob0 +// real, allocatable, target :: glob1 +// contains +// subroutine test(arg0, arg1) +// real, allocatable, target :: arg0 +// real, allocatable, target :: arg1 +// end subroutine +// end module + +// CHECK-DAG: arg0#0 <-> arg1#0: MayAlias +// CHECK-DAG: arg0#0 <-> glob0#0: MayAlias +// CHECK-DAG: glob0#0 <-> glob1#0: NoAlias + +func.func @_QMmPtest(%arg0: !fir.ref>> {fir.bindc_name = "arg0", fir.target}, %arg1: !fir.ref>> {fir.bindc_name = "arg1", fir.target}) { + %0 = fir.dummy_scope : !fir.dscope + + %10:2 = hlfir.declare %arg0 dummy_scope %0 {test.ptr="arg0", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmFtestEarg0"} : (!fir.ref>>, !fir.dscope) -> (!fir.ref>>, !fir.ref>>) + %11:2 = hlfir.declare %arg1 dummy_scope %0 {test.ptr="arg1", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmFtestEarg1"} : (!fir.ref>>, !fir.dscope) -> (!fir.ref>>, !fir.ref>>) + + %20 = fir.address_of(@_QMmEglob0) : !fir.ref>> + %21:2 = hlfir.declare %20 {test.ptr="glob0", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmEglob0"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) + %22 = fir.address_of(@_QMmEglob1) : !fir.ref>> + %23:2 = hlfir.declare %22 {test.ptr="glob1", fortran_attrs = #fir.var_attrs, uniq_name = "_QMmEglob1"} : (!fir.ref>>) -> (!fir.ref>>, !fir.ref>>) + + return +}