diff --git a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h --- a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h +++ b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h @@ -1,4 +1,4 @@ -//===- AliasAnalysis.h - Alias Analysis in FIR -----------------*- C++ -*-===// +//===-- AliasAnalysis.h - Alias Analysis in FIR -----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,10 +6,14 @@ // //===----------------------------------------------------------------------===// -#ifndef FIR_ANALYSIS_ALIASANALYSIS_H_ -#define FIR_ANALYSIS_ALIASANALYSIS_H_ +#ifndef FORTRAN_OPTIMIZER_ANALYSIS_ALIASANALYSIS_H +#define FORTRAN_OPTIMIZER_ANALYSIS_ALIASANALYSIS_H +#include "flang/Common/enum-class.h" +#include "flang/Common/enum-set.h" #include "mlir/Analysis/AliasAnalysis.h" +#include "mlir/IR/Value.h" +#include "llvm/ADT/PointerUnion.h" namespace fir { @@ -17,13 +21,77 @@ // AliasAnalysis //===----------------------------------------------------------------------===// class AliasAnalysis { + // Structures to describe the memory source of a value. + + /// Kind of the memory source referenced by a value. + ENUM_CLASS(SourceKind, + /// Unique memory allocated by an operation, e.g. + /// by fir::AllocaOp or fir::AllocMemOp. + Allocate, + /// A global object allocated statically on the module level. + Global, + /// Memory allocated outside of a function and passed + /// to the function as a by-ref argument. + Argument, + /// Represents memory allocated by unknown means and + /// with the memory address defined by a memory reading + /// operation (e.g. fir::LoadOp). + Indirect, + /// Represents memory allocated by unknown means. + Unknown); + + /// Attributes of the memory source object. + ENUM_CLASS(Attribute, Target, Pointer, IntentIn); + + struct Source { + using SourceUnion = llvm::PointerUnion; + using Attributes = Fortran::common::EnumSet; + + /// Source definition of a value. + SourceUnion u; + /// Kind of the memory source. + SourceKind kind; + /// Value type of the source definition. + mlir::Type valueType; + /// Attributes of the memory source object, e.g. Target. + Attributes attributes; + + /// Print information about the memory source to `os`. + void print(llvm::raw_ostream &os) const; + + /// Return true, if Target or Pointer attribute is set. + bool isTargetOrPointer() const; + + /// Return true, if the memory source's `valueType` is a reference type + /// to an object of derived type that contains a component with POINTER + /// attribute. + bool isRecordWithPointerComponent() const; + + /// Return true, if `ty` is a reference type to a boxed + /// POINTER object or a raw fir::PointerType. + static bool isPointerReference(mlir::Type ty); + }; + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, + const AliasAnalysis::Source &op); + public: /// Given two values, return their aliasing behavior. mlir::AliasResult alias(mlir::Value lhs, mlir::Value rhs); /// Return the modify-reference behavior of `op` on `location`. mlir::ModRefResult getModRef(mlir::Operation *op, mlir::Value location); + + /// Return the memory source of a value. + Source getSource(mlir::Value); }; + +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, + const AliasAnalysis::Source &op) { + op.print(os); + return os; +} + } // namespace fir -#endif // FIR_ANALYSIS_ALIASANALYSIS_H_ +#endif // FORTRAN_OPTIMIZER_ANALYSIS_ALIASANALYSIS_H diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp --- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp +++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp @@ -7,7 +7,15 @@ //===----------------------------------------------------------------------===// #include "flang/Optimizer/Analysis/AliasAnalysis.h" +#include "flang/Optimizer/Dialect/FIROps.h" +#include "flang/Optimizer/Dialect/FIROpsSupport.h" +#include "flang/Optimizer/Dialect/FIRType.h" +#include "mlir/Analysis/AliasAnalysis.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/Value.h" #include "mlir/Interfaces/SideEffectInterfaces.h" +#include "llvm/ADT/TypeSwitch.h" +#include "llvm/Support/Casting.h" using namespace mlir; @@ -15,13 +23,119 @@ // AliasAnalysis: alias //===----------------------------------------------------------------------===// +static bool isDummyArgument(mlir::Value v) { + auto blockArg{v.dyn_cast()}; + if (!blockArg) + return false; + + return blockArg.getOwner()->isEntryBlock(); +} + namespace fir { + +void AliasAnalysis::Source::print(llvm::raw_ostream &os) const { + if (auto v = llvm::dyn_cast(u)) + os << v; + else if (auto gbl = llvm::dyn_cast(u)) + os << gbl; + os << " SourceKind: " << EnumToString(kind); + os << " Type: " << valueType << " "; + attributes.Dump(os, EnumToString); +} + +bool AliasAnalysis::Source::isPointerReference(mlir::Type ty) { + auto eleTy = fir::dyn_cast_ptrEleTy(ty); + if (!eleTy) + return false; + + return fir::isPointerType(eleTy) || eleTy.isa(); +} + +bool AliasAnalysis::Source::isTargetOrPointer() const { + return attributes.test(Attribute::Pointer) || + attributes.test(Attribute::Target); +} + +bool AliasAnalysis::Source::isRecordWithPointerComponent() const { + auto eleTy = fir::dyn_cast_ptrEleTy(valueType); + if (!eleTy) + return false; + // TO DO: Look for pointer components + return eleTy.isa(); +} + AliasResult AliasAnalysis::alias(Value lhs, Value rhs) { - // This is for now a mock analysis - if (lhs == rhs) { - return AliasResult::MustAlias; + auto lhsSrc = getSource(lhs); + auto rhsSrc = getSource(rhs); + + // Indirect case currently not handled. Conservatively assume + // it aliases with everything + if (lhsSrc.kind == SourceKind::Indirect || + lhsSrc.kind == SourceKind::Unknown || + rhsSrc.kind == SourceKind::Indirect || rhsSrc.kind == SourceKind::Unknown) + return AliasResult::MayAlias; + + if (lhsSrc.kind == rhsSrc.kind) { + if (lhsSrc.u == rhsSrc.u) + return AliasResult::MustAlias; + + // Allocate and global memory address cannot physically alias + if (lhsSrc.kind == SourceKind::Allocate || + lhsSrc.kind == SourceKind::Global) + return AliasResult::NoAlias; + + assert(lhsSrc.kind == SourceKind::Argument && + "unexpected memory source kind"); + + // Dummy TARGET/POINTER arguments may alias. + if (lhsSrc.isTargetOrPointer() && rhsSrc.isTargetOrPointer()) + 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 ((lhsSrc.isRecordWithPointerComponent() && rhsSrc.isTargetOrPointer()) || + (rhsSrc.isRecordWithPointerComponent() && lhsSrc.isTargetOrPointer()) || + (lhsSrc.isRecordWithPointerComponent() && + rhsSrc.isRecordWithPointerComponent())) + return AliasResult::MayAlias; + + return AliasResult::NoAlias; } - return AliasResult::MayAlias; + + assert(lhsSrc.kind != rhsSrc.kind && "memory source kinds must be the same"); + + Source *src1, *src2; + if (lhsSrc.kind < rhsSrc.kind) { + src1 = &lhsSrc; + src2 = &rhsSrc; + } else { + src1 = &rhsSrc; + src2 = &lhsSrc; + } + + assert(src2->kind <= SourceKind::Argument && "unexpected memory source kind"); + if (src1->kind == SourceKind::Allocate) + return AliasResult::NoAlias; + + assert(src1->kind == SourceKind::Global && + src2->kind == SourceKind::Argument && + "unexpected memory source kinds"); + + // Dummy TARGET/POINTER argument may alias with a global TARGET/POINTER. + if (src1->isTargetOrPointer() && src2->isTargetOrPointer()) + 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 ((src1->isRecordWithPointerComponent() && src2->isTargetOrPointer()) || + (src2->isRecordWithPointerComponent() && src1->isTargetOrPointer()) || + (src1->isRecordWithPointerComponent() && + src2->isRecordWithPointerComponent())) + return AliasResult::MayAlias; + + return AliasResult::NoAlias; } //===----------------------------------------------------------------------===// @@ -30,7 +144,8 @@ /// This is mostly inspired by MLIR::LocalAliasAnalysis with 2 notable /// differences 1) Regions are not handled here but will be handled by a data -/// flow analysis to come 2) Allocate and Free effects are considered modifying +/// flow analysis to come 2) Allocate and Free effects are considered +/// modifying ModRefResult AliasAnalysis::getModRef(Operation *op, Value location) { MemoryEffectOpInterface interface = dyn_cast(op); if (!interface) @@ -54,14 +169,77 @@ continue; // Merge in the corresponding mod or ref for this effect. - if (isa(effect.getEffect())) { + if (isa(effect.getEffect())) result = result.merge(ModRefResult::getRef()); - } else { + else result = result.merge(ModRefResult::getMod()); - } + if (result.isModAndRef()) break; } return result; } + +AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) { + auto *defOp = v.getDefiningOp(); + SourceKind type{SourceKind::Unknown}; + mlir::Type ty; + bool breakFromLoop{false}; + mlir::SymbolRefAttr global; + Source::Attributes attributes; + while (defOp && !breakFromLoop) { + ty = defOp->getResultTypes()[0]; + llvm::TypeSwitch(defOp) + .Case([&](auto op) { + // Unique memory allocation. + type = SourceKind::Allocate; + breakFromLoop = true; + }) + .Case([&](auto op) { + // Skip ConvertOp's and track further through the operand. + v = op->getOperand(0); + defOp = v.getDefiningOp(); + }) + .Case([&](auto op) { + // No further tracking for addresses loaded from memory (e.g. a box) + // right now. + type = SourceKind::Indirect; + breakFromLoop = true; + }) + .Case([&](auto op) { + // Address of a global scope object. + type = SourceKind::Global; + ty = v.getType(); + if (fir::valueHasFirAttribute(v, + fir::GlobalOp::getTargetAttrNameStr())) + attributes.set(Attribute::Target); + + if (Source::isPointerReference(ty)) + attributes.set(Attribute::Pointer); + global = llvm::cast(op).getSymbol(); + breakFromLoop = true; + }) + .Default([&](auto op) { + defOp = nullptr; + breakFromLoop = true; + }); + } + if (!defOp && type == SourceKind::Unknown) + // Check if the memory source is coming through a dummy argument. + if (isDummyArgument(v)) { + type = SourceKind::Argument; + ty = v.getType(); + if (fir::valueHasFirAttribute(v, fir::getTargetAttrName())) + attributes.set(Attribute::Target); + + if (Source::isPointerReference(ty)) + attributes.set(Attribute::Pointer); + } + + if (type == SourceKind::Global) + return {global, type, ty, attributes}; + + return {v, type, ty, attributes}; +} + } // namespace fir diff --git a/flang/test/lib/Analysis/AliasAnalysis/alias-analysis-1.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-1.fir rename from flang/test/lib/Analysis/AliasAnalysis/alias-analysis-1.fir rename to flang/test/Analysis/AliasAnalysis/alias-analysis-1.fir --- a/flang/test/lib/Analysis/AliasAnalysis/alias-analysis-1.fir +++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-1.fir @@ -1,7 +1,9 @@ -// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file 2>&1 | FileCheck %s +// Use --mlir-disable-threading so that the AA queries are serialized +// as well as its diagnostic output. +// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s // CHECK-LABEL: Testing : "_QPtest" -// CHECK-DAG: alloca_1#0 <-> address_of#0: MayAlias +// CHECK-DAG: alloca_1#0 <-> address_of#0: NoAlias func.func @_QPtest(%arg1: !fir.ref) { %c1_i32 = arith.constant 1 : i32 %0 = fir.alloca () -> () {test.ptr = "alloca_1"} @@ -14,8 +16,5 @@ return } -// ----- func.func private @_QPs(%arg0: () -> ()) - -// ----- func.func private @_QPf() -> i32 diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir new file mode 100644 --- /dev/null +++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir @@ -0,0 +1,173 @@ +// Use --mlir-disable-threading so that the AA queries are serialized +// as well as its diagnostic output. +// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s + +// CHECK-LABEL: Testing : "_QFPtest" + +// p1.addr and p2.addr result from 2 different allocas +// They cannot physically alias +// CHECK-DAG: p1.addr#0 <-> p2.addr#0: NoAlias + +// p1.addr and p2.addr could both be wrapped inside boxes +// CHECK-DAG: p1.addr#0 <-> boxp1.addr#0: MayAlias +// CHECK-DAG: p2.addr#0 <-> boxp1.addr#0: MayAlias +// CHECK-DAG: p1.addr#0 <-> arg2.addr#0: MayAlias +// CHECK-DAG: p2.addr#0 <-> arg2.addr#0: MayAlias + +// p1.addr and p2.addr are the result of an allocation +// They cannot physically alias with an argument +// CHECK-DAG: p1.addr#0 <-> func.region0#0: NoAlias +// CHECK-DAG: p2.addr#0 <-> func.region0#0: NoAlias +// CHECK-DAG: p1.addr#0 <-> func.region0#1: NoAlias +// CHECK-DAG: p2.addr#0 <-> func.region0#1: NoAlias +// CHECK-DAG: p1.addr#0 <-> func.region0#2: NoAlias +// CHECK-DAG: p2.addr#0 <-> func.region0#2: NoAlias + +// All arguments are either pointers or targets +// A pointer in a box may alias with both +// CHECK-DAG: boxp1.addr#0 <-> func.region0#0: MayAlias +// CHECK-DAG: boxp1.addr#0 <-> func.region0#1: MayAlias +// CHECK-DAG: boxp1.addr#0 <-> func.region0#2: MayAlias + +// A target dummy may alias with another target +// CHECK-DAG: func.region0#0 <-> func.region0#1: MayAlias + +// arg2 is a reference to a pointer. Modifying arg2 could +// modify a target with a pointer component +// CHECK-DAG: func.region0#0 <-> func.region0#2: MayAlias +// CHECK-DAG: func.region0#1 <-> func.region0#2: MayAlias + +// However, the address wrapped by arg2, can alias with any target or +// pointer arguments +// CHECK-DAG: arg2.addr#0 <-> func.region0#0: MayAlias +// CHECK-DAG: arg2.addr#0 <-> func.region0#1: MayAlias +// CHECK-DAG: arg2.addr#0 <-> func.region0#2: MayAlias +// CHECK-DAG: boxp1.addr#0 <-> arg2.addr#0: MayAlias + +func.func @_QFPtest(%arg0: !fir.ref {fir.bindc_name = "v1", fir.target}, %arg1: !fir.ref {fir.bindc_name = "v2", fir.target}, %arg2: !fir.ref>> ) attributes {test.ptr = "func"} { + + %1 = fir.alloca !fir.ptr {test.ptr = "p1.addr"} + %2 = fir.zero_bits !fir.ptr + fir.store %2 to %1 : !fir.ref> + + %4 = fir.alloca !fir.ptr {test.ptr = "p2.addr"} + fir.store %2 to %4 : !fir.ref> + + %5 = fir.convert %arg0 : (!fir.ref) -> !fir.ptr + fir.store %5 to %1 : !fir.ref> + + %6 = fir.convert %arg1 : (!fir.ref) -> !fir.ptr + fir.store %6 to %4 : !fir.ref> + + %0 = fir.alloca !fir.box> {bindc_name = "p1", uniq_name = "_QFtestEp1"} + %7 = fir.load %1 : !fir.ref> + %8 = fir.embox %7 : (!fir.ptr) -> !fir.box> + fir.store %8 to %0 : !fir.ref>> + + %3 = fir.alloca !fir.box> {bindc_name = "p2", uniq_name = "_QFtestEp2"} + %9 = fir.load %4 : !fir.ref> + %10 = fir.embox %9 : (!fir.ptr) -> !fir.box> + fir.store %10 to %3 : !fir.ref>> + + %11 = fir.load %0 : !fir.ref>> + %12 = fir.box_addr %11 {test.ptr = "boxp1.addr"} : (!fir.box>) -> !fir.ptr + fir.store %12 to %1 : !fir.ref> + + %13 = fir.load %3 : !fir.ref>> + %14 = fir.box_addr %13 : (!fir.box>) -> !fir.ptr + fir.store %14 to %4 : !fir.ref> + + %15 = fir.load %arg2 : !fir.ref>> + %16 = fir.box_addr %15 {test.ptr = "arg2.addr"} : (!fir.box>) -> !fir.ptr + return +} + +// ----- + +// CHECK-LABEL: Testing : "_QFPtest2" + +// subroutine test2(v1,p1,p2) +// real, target :: v1 +// real, pointer :: p1, p2 +// ... +// end subroutine + +// Direct access to dummy POINTER references can modify other dummy POINTER references +// CHECK-DAG: func.region0#1 <-> func.region0#2: MayAlias + +// They can also modify targets that have pointer components +// CHECK-DAG: func.region0#0 <-> func.region0#1: MayAlias +// CHECK-DAG: func.region0#0 <-> func.region0#2: MayAlias + +func.func @_QFPtest2(%arg0: !fir.ref {fir.bindc_name = "v1", fir.target}, %arg1: !fir.ref>>, %arg2: !fir.ref> ) attributes {test.ptr = "func"} { + return +} + +// ----- + +// CHECK-LABEL: Testing : "_QFPtest3" + +// module pointers +// real, pointer :: p +// end module +// +// program main +// use pointers +// real, target :: var1 = 1, var2 =2 +// p => var1 +// +// call test3(p) +// +// contains +// subroutine test3(p1) +// real, pointer :: p1 +// p1 => var2 +// print *, p +// end subroutine +// end + +// The global pointer p may alias with the dummy argument p1 +// but not with the dummy arg1 which is just a regular dummy +// CHECK-DAG: p#0 <-> func.region0#0: MayAlias +// CHECK-DAG: p#0 <-> func.region0#1: NoAlias + +// FIXME: p and p1 are pointers, they cannot alias with a wrapped address. +// Only the addresses they wrap could alias with the address wrapped by the box +// CHECK-DAG: p#0 <-> box.addr#0: MayAlias +// CHECK-DAG: box.addr#0 <-> func.region0#0: MayAlias + +// var2, although it is a target, cannot alias with p +// A modification of p would only make them point to a new target but not modify it +// CHECK-DAG: var2#0 <-> p#0: NoAlias +// It can alias with p1, if p1 is a pointer component +// CHECK-DAG: var2#0 <-> func.region0#0: MayAlias +// It can alias with a box.addr +// CHECK-DAG: var2#0 <-> box.addr#0: MayAlias + +// A global may not alias with a dummy +// CHECK-DAG: var2#0 <-> func.region0#1: NoAlias + +// FIXME: a pointer may only alias with a target but arg1 is a regular dummy +// CHECK-DAG: box.addr#0 <-> func.region0#1: MayAlias + +// Dummy argument do not alias +// CHECK-DAG: func.region0#0 <-> func.region0#1: NoAlias + +fir.global @_QMpointersEp : !fir.box> { + %0 = fir.zero_bits !fir.ptr + %1 = fir.embox %0 : (!fir.ptr) -> !fir.box> + fir.has_value %1 : !fir.box> +} + +fir.global internal @_QFEvar2 target : f32 { + %cst = arith.constant 2.000000e+00 : f32 + fir.has_value %cst : f32 +} + +func.func @_QFPtest3(%arg0: !fir.ref>> {fir.bindc_name = "p1"}, %arg1: !fir.ref) attributes {test.ptr = "func"} { + %4 = fir.address_of(@_QFEvar2) {test.ptr = "var2"} : !fir.ref + %5 = fir.address_of(@_QMpointersEp) {test.ptr = "p"} : !fir.ref>> + %6 = fir.embox %4 : (!fir.ref) -> !fir.box> + %13 = fir.box_addr %6 {test.ptr = "box.addr"} : (!fir.box>) -> !fir.ptr + return +} diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir new file mode 100644 --- /dev/null +++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir @@ -0,0 +1,157 @@ +// Use --mlir-disable-threading so that the AA queries are serialized +// as well as its diagnostic output. +// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s + +// module m +// type t +// real, pointer :: pointer_component +// end type t +// type(t) :: a +// contains +// subroutine test(pointer_dummy, x) +// real, pointer :: pointer_dummy +// real, target :: x +// pointer_dummy => x +// call test2(a%pointer_component) +// end subroutine test +// end module m + +// A composite with a pointer component may alias with a dummy pointer +// 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 + +// FIXME: pointer_dummy's box cannot alias with raw reference to f32 (x), so MayAlias below must be NoAlias: +// CHECK: func.region0#0 <-> func.region0#1: MayAlias + +fir.global @_QMmEa : !fir.type<_QMmTt{pointer_component:!fir.box>}> { + %0 = fir.undefined !fir.type<_QMmTt{pointer_component:!fir.box>}> + fir.has_value %0 : !fir.type<_QMmTt{pointer_component:!fir.box>}> +} +func.func @_QMmPtest(%arg0: !fir.ref>> {fir.bindc_name = "pointer_dummy"}, %arg1: !fir.ref {fir.bindc_name = "x", fir.target}) attributes {test.ptr = "func"} { + %0 = fir.address_of(@_QMmEa) {test.ptr = "a"} : !fir.ref>}>> + %1 = fir.embox %arg1 : (!fir.ref) -> !fir.box> + fir.store %1 to %arg0 : !fir.ref>> + %2 = fir.field_index pointer_component, !fir.type<_QMmTt{pointer_component:!fir.box>}> + %3 = fir.coordinate_of %0, %2 : (!fir.ref>}>>, !fir.field) -> !fir.ref>> + %4 = fir.load %3 : !fir.ref>> + %5 = fir.box_addr %4 : (!fir.box>) -> !fir.ptr + %6 = fir.convert %5 : (!fir.ptr) -> !fir.ref + fir.call @_QPtest2(%6) fastmath : (!fir.ref) -> () + return +} +func.func private @_QPtest2(!fir.ref) + +// ----- + +// A composite with a pointer component may alias with a dummy +// argument of composite type with a pointer component: +// module m +// type t +// real, pointer :: pointer_component +// end type t +// type(t) :: a +// contains +// subroutine test(b, x) +// type(t) :: b +// real, target :: x +// a%pointer_component => x +// call test2(b%pointer_component) +// end subroutine test +// end module m + +// CHECK-LABEL: Testing : "_QMmPtest" +// CHECK: a#0 <-> func.region0#0: MayAlias + +fir.global @_QMmEa : !fir.type<_QMmTt{pointer_component:!fir.box>}> { + %0 = fir.undefined !fir.type<_QMmTt{pointer_component:!fir.box>}> + fir.has_value %0 : !fir.type<_QMmTt{pointer_component:!fir.box>}> +} +func.func @_QMmPtest(%arg0: !fir.ref>}>> {fir.bindc_name = "b"}, %arg1: !fir.ref {fir.bindc_name = "x", fir.target}) attributes {test.ptr = "func"} { + %0 = fir.address_of(@_QMmEa) {test.ptr = "a"} : !fir.ref>}>> + %1 = fir.field_index pointer_component, !fir.type<_QMmTt{pointer_component:!fir.box>}> + %2 = fir.coordinate_of %0, %1 : (!fir.ref>}>>, !fir.field) -> !fir.ref>> + %3 = fir.embox %arg1 : (!fir.ref) -> !fir.box> + fir.store %3 to %2 : !fir.ref>> + %4 = fir.field_index pointer_component, !fir.type<_QMmTt{pointer_component:!fir.box>}> + %5 = fir.coordinate_of %arg0, %4 : (!fir.ref>}>>, !fir.field) -> !fir.ref>> + %6 = fir.load %5 : !fir.ref>> + %7 = fir.box_addr %6 : (!fir.box>) -> !fir.ptr + %8 = fir.convert %7 : (!fir.ptr) -> !fir.ref + fir.call @_QPtest2(%8) fastmath : (!fir.ref) -> () + return +} +func.func private @_QPtest2(!fir.ref) + +// ----- + +// Two dummy arguments of composite type with a pointer component +// may alias each other: +// module m +// type t +// real, pointer :: pointer_component +// end type t +// contains +// subroutine test(a, b, x) +// type(t) :: a, b +// real, target :: x +// a%pointer_component => x +// call test2(b%pointer_component) +// end subroutine test +// end module m + +// CHECK-LABEL: Testing : "_QMmPtest" +// CHECK: func.region0#0 <-> func.region0#1: MayAlias + +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>}> + %1 = fir.coordinate_of %arg0, %0 : (!fir.ref>}>>, !fir.field) -> !fir.ref>> + %2 = fir.embox %arg2 : (!fir.ref) -> !fir.box> + fir.store %2 to %1 : !fir.ref>> + %3 = fir.field_index pointer_component, !fir.type<_QMmTt{pointer_component:!fir.box>}> + %4 = fir.coordinate_of %arg1, %3 : (!fir.ref>}>>, !fir.field) -> !fir.ref>> + %5 = fir.load %4 : !fir.ref>> + %6 = fir.box_addr %5 : (!fir.box>) -> !fir.ptr + %7 = fir.convert %6 : (!fir.ptr) -> !fir.ref + fir.call @_QPtest2(%7) fastmath : (!fir.ref) -> () + return +} +func.func private @_QPtest2(!fir.ref) + +// ----- + +// Two dummy arguments of composite type consisting of an allocatable +// component cannot alias: +// module m +// type t +// real, allocatable :: allocatable_component +// end type t +// contains +// subroutine test(a, b) +// type(t) :: a, b +// allocate(a%allocatable_component) +// call test2(b%allocatable_component) +// end subroutine test +// end module m + +// CHECK-LABEL: Testing : "_QMmPtest" +// FIXME: MayAlias must be NoAlias +// CHECK: func.region0#0 <-> func.region0#1: MayAlias + +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>}> + %1 = fir.coordinate_of %arg0, %0 : (!fir.ref>}>>, !fir.field) -> !fir.ref>> + %2 = fir.allocmem f32 {uniq_name = "_QMmEallocatable_component.alloc"} + %3 = fir.embox %2 : (!fir.heap) -> !fir.box> + fir.store %3 to %1 : !fir.ref>> + %4 = fir.field_index allocatable_component, !fir.type<_QMmTt{allocatable_component:!fir.box>}> + %5 = fir.coordinate_of %arg1, %4 : (!fir.ref>}>>, !fir.field) -> !fir.ref>> + %6 = fir.load %5 : !fir.ref>> + %7 = fir.box_addr %6 : (!fir.box>) -> !fir.heap + %8 = fir.convert %7 : (!fir.heap) -> !fir.ref + fir.call @_QPtest2(%8) fastmath : (!fir.ref) -> () + return +} +func.func private @_QPtest2(!fir.ref)