From ee407e17a3a4986bab49272665abc9973d78691d Mon Sep 17 00:00:00 2001 From: Renaud Kauffmann <50748669+Renaud-K@users.noreply.github.com> Date: Thu, 16 May 2024 08:49:44 -0700 Subject: [PATCH] [flang] AliasAnalysis: More formally define and distinguish between data and non-data (#91020) This PR is an implementation for changes proposed in https://discourse.llvm.org/t/rfc-distinguish-between-data-and-non-data-in-fir-alias-analysis/78759 Test updates were made when the query was on the wrong reference. So, it is my hope that this will clear ambiguity on the nature of the queries from here on. There are also some TODOs that were addressed. It also partly implements what https://github.com/llvm/llvm-project/pull/87723 is attempting to accomplish. At least, on a point-to-point query between references, the distinction is made. To apply it to TBAA, would be another PR. Note that, the changes were minimal in the TBAA code to retain the current results. --- .../flang/Optimizer/Analysis/AliasAnalysis.h | 95 ++++++++++- .../lib/Optimizer/Analysis/AliasAnalysis.cpp | 157 +++++++----------- .../lib/Optimizer/Transforms/AddAliasTags.cpp | 17 +- .../AliasAnalysis/alias-analysis-2.fir | 37 +++-- .../AliasAnalysis/alias-analysis-3.fir | 4 +- .../AliasAnalysis/alias-analysis-9.fir | 59 +++++++ 6 files changed, 248 insertions(+), 121 deletions(-) create mode 100644 flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir diff --git a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h index dfcafe88fee1b5..40fd1705115a06 100644 --- a/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h +++ b/flang/include/flang/Optimizer/Analysis/AliasAnalysis.h @@ -36,9 +36,6 @@ struct AliasAnalysis { /// Represents memory allocated outside of a function /// and passed to the function via host association tuple. HostAssoc, - /// Represents direct memory access whose source cannot be further - /// determined - Direct, /// Represents memory allocated by unknown means and /// with the memory address defined by a memory reading /// operation (e.g. fir::LoadOp). @@ -50,12 +47,85 @@ struct AliasAnalysis { /// Attributes of the memory source object. ENUM_CLASS(Attribute, Target, Pointer, IntentIn); + // See + // https://discourse.llvm.org/t/rfc-distinguish-between-data-and-non-data-in-fir-alias-analysis/78759/1 + // + // It is possible, while following the source of a memory reference through + // the use-def chain, to arrive at the same origin, even though the starting + // points were known to not alias. + // + // clang-format off + // Example: + // ------------------- test.f90 -------------------- + // module top + // real, pointer :: a(:) + // end module + // + // subroutine test() + // use top + // a(1) = 1 + // end subroutine + // ------------------------------------------------- + // + // flang-new -fc1 -emit-fir test.f90 -o test.fir + // + // ------------------- test.fir -------------------- + // fir.global @_QMtopEa : !fir.box>> + // + // func.func @_QPtest() { + // %c1 = arith.constant 1 : index + // %cst = arith.constant 1.000000e+00 : f32 + // %0 = fir.address_of(@_QMtopEa) : !fir.ref>>> + // %1 = fir.declare %0 {fortran_attrs = #fir.var_attrs, uniq_name = "_QMtopEa"} : (!fir.ref>>>) -> !fir.ref>>> + // %2 = fir.load %1 : !fir.ref>>> + // ... + // %5 = fir.array_coor %2 %c1 : (!fir.box>>, !fir.shift<1>, index) -> !fir.ref + // fir.store %cst to %5 : !fir.ref + // return + // } + // ------------------------------------------------- + // + // With high level operations, such as fir.array_coor, it is possible to + // reach into the data wrapped by the box (the descriptor). Therefore when + // asking about the memory source of %5, we are really asking about the + // source of the data of box %2. + // + // When asking about the source of %0 which is the address of the box, we + // reach the same source as in the first case: the global @_QMtopEa. Yet one + // source refers to the data while the other refers to the address of the box + // itself. + // + // To distinguish between the two, the isData flag has been added, whereby + // data is defined as any memory reference that is not a box reference. + // Additionally, because it is relied on in HLFIR lowering, we allow querying + // on a box SSA value, which is interpreted as querying on its data. + // + // So in the above example, !fir.ref and !fir.box>> is data, + // while !fir.ref>>> is not data. + + // This also applies to function arguments. In the example below, %arg0 + // is data, %arg1 is not data but a load of %arg1 is. + // + // func.func @_QFPtest2(%arg0: !fir.ref, %arg1: !fir.ref>> ) { + // %0 = fir.load %arg1 : !fir.ref>> + // ... } + // + // clang-format on + struct Source { using SourceUnion = llvm::PointerUnion; using Attributes = Fortran::common::EnumSet; - /// Source definition of a value. - SourceUnion u; + struct SourceOrigin { + /// Source definition of a value. + SourceUnion u; + + /// Whether the source was reached following data or box reference + bool isData{false}; + }; + + SourceOrigin origin; + /// Kind of the memory source. SourceKind kind; /// Value type of the source definition. @@ -77,6 +147,12 @@ struct AliasAnalysis { /// attribute. bool isRecordWithPointerComponent() const; + bool isDummyArgument() const; + bool isData() const; + bool isBoxData() const; + + mlir::Type getType() 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); @@ -95,6 +171,15 @@ struct AliasAnalysis { Source getSource(mlir::Value); }; +inline bool operator==(const AliasAnalysis::Source::SourceOrigin &lhs, + const AliasAnalysis::Source::SourceOrigin &rhs) { + return lhs.u == rhs.u && lhs.isData == rhs.isData; +} +inline bool operator!=(const AliasAnalysis::Source::SourceOrigin &lhs, + const AliasAnalysis::Source::SourceOrigin &rhs) { + return !(lhs == rhs); +} + inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const AliasAnalysis::Source &op) { op.print(os); diff --git a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp index ed1101dc5e8d88..9d0d706a85c5e1 100644 --- a/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp +++ b/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp @@ -33,11 +33,9 @@ static bool isDummyArgument(mlir::Value v) { if (!blockArg) return false; - mlir::Block *owner = blockArg.getOwner(); - if (!owner->isEntryBlock() || - !mlir::isa(owner->getParentOp())) - return false; - return true; + auto *owner{blockArg.getOwner()}; + return owner->isEntryBlock() && + mlir::isa(owner->getParentOp()); } /// Temporary function to skip through all the no op operations @@ -58,12 +56,17 @@ static mlir::Value getOriginalDef(mlir::Value v) { namespace fir { void AliasAnalysis::Source::print(llvm::raw_ostream &os) const { - if (auto v = llvm::dyn_cast(u)) + if (auto v = llvm::dyn_cast(origin.u)) os << v; - else if (auto gbl = llvm::dyn_cast(u)) + else if (auto gbl = llvm::dyn_cast(origin.u)) os << gbl; os << " SourceKind: " << EnumToString(kind); os << " Type: " << valueType << " "; + if (origin.isData) { + os << " following data "; + } else { + os << " following box reference "; + } attributes.Dump(os, EnumToString); } @@ -80,6 +83,19 @@ bool AliasAnalysis::Source::isTargetOrPointer() const { attributes.test(Attribute::Target); } +bool AliasAnalysis::Source::isDummyArgument() const { + if (auto v = origin.u.dyn_cast()) { + return ::isDummyArgument(v); + } + return false; +} + +bool AliasAnalysis::Source::isData() const { return origin.isData; } +bool AliasAnalysis::Source::isBoxData() const { + return mlir::isa(fir::unwrapRefType(valueType)) && + origin.isData; +} + bool AliasAnalysis::Source::isRecordWithPointerComponent() const { auto eleTy = fir::dyn_cast_ptrEleTy(valueType); if (!eleTy) @@ -92,70 +108,35 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) { auto lhsSrc = getSource(lhs); auto rhsSrc = getSource(rhs); bool approximateSource = lhsSrc.approximateSource || rhsSrc.approximateSource; - LLVM_DEBUG(llvm::dbgs() << "AliasAnalysis::alias\n"; + LLVM_DEBUG(llvm::dbgs() << "\nAliasAnalysis::alias\n"; llvm::dbgs() << " lhs: " << lhs << "\n"; llvm::dbgs() << " lhsSrc: " << lhsSrc << "\n"; llvm::dbgs() << " rhs: " << rhs << "\n"; - llvm::dbgs() << " rhsSrc: " << rhsSrc << "\n"; - llvm::dbgs() << "\n";); + llvm::dbgs() << " rhsSrc: " << rhsSrc << "\n";); // Indirect case currently not handled. Conservatively assume // it aliases with everything - if (lhsSrc.kind > SourceKind::Direct || rhsSrc.kind > SourceKind::Direct) { + if (lhsSrc.kind >= SourceKind::Indirect || + rhsSrc.kind >= SourceKind::Indirect) { return AliasResult::MayAlias; } - // SourceKind::Direct is set for the addresses wrapped in a global boxes. - // ie: fir.global @_QMpointersEp : !fir.box> - // Though nothing is known about them, they would only alias with targets or - // pointers - bool directSourceToNonTargetOrPointer = false; - if (lhsSrc.u != rhsSrc.u || lhsSrc.kind != rhsSrc.kind) { - if ((lhsSrc.kind == SourceKind::Direct && !rhsSrc.isTargetOrPointer()) || - (rhsSrc.kind == SourceKind::Direct && !lhsSrc.isTargetOrPointer())) - directSourceToNonTargetOrPointer = true; - } - - if (lhsSrc.kind == SourceKind::Direct || - rhsSrc.kind == SourceKind::Direct) { - if (!directSourceToNonTargetOrPointer) - return AliasResult::MayAlias; - } - if (lhsSrc.kind == rhsSrc.kind) { - if (lhsSrc.u == rhsSrc.u) { + if (lhsSrc.origin == rhsSrc.origin) { + LLVM_DEBUG(llvm::dbgs() + << " aliasing because same source kind and origin\n"); if (approximateSource) return AliasResult::MayAlias; return AliasResult::MustAlias; } // Two host associated accesses may overlap due to an equivalence. - if (lhsSrc.kind == SourceKind::HostAssoc) + if (lhsSrc.kind == SourceKind::HostAssoc) { + LLVM_DEBUG(llvm::dbgs() << " aliasing because of host association\n"); return AliasResult::MayAlias; - - // Allocate and global memory address cannot physically alias - if (lhsSrc.kind == SourceKind::Allocate || - lhsSrc.kind == SourceKind::Global) - return AliasResult::NoAlias; - - // 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; + } } - assert(lhsSrc.kind != rhsSrc.kind && "memory source kinds must be different"); - Source *src1, *src2; if (lhsSrc.kind < rhsSrc.kind) { src1 = &lhsSrc; @@ -186,8 +167,11 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) { } // Dummy TARGET/POINTER argument may alias with a global TARGET/POINTER. - if (src1->isTargetOrPointer() && src2->isTargetOrPointer()) + if (src1->isTargetOrPointer() && src2->isTargetOrPointer() && + 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 @@ -195,8 +179,10 @@ AliasResult AliasAnalysis::alias(Value lhs, Value rhs) { if ((src1->isRecordWithPointerComponent() && src2->isTargetOrPointer()) || (src2->isRecordWithPointerComponent() && src1->isTargetOrPointer()) || (src1->isRecordWithPointerComponent() && - src2->isRecordWithPointerComponent())) + src2->isRecordWithPointerComponent())) { + LLVM_DEBUG(llvm::dbgs() << " aliasing because of pointer components\n"); return AliasResult::MayAlias; + } return AliasResult::NoAlias; } @@ -262,7 +248,10 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) { mlir::Type ty; bool breakFromLoop{false}; bool approximateSource{false}; - bool followBoxAddr{mlir::isa(v.getType())}; + bool followBoxData{mlir::isa(v.getType())}; + bool isBoxRef{fir::isa_ref_type(v.getType()) && + mlir::isa(fir::unwrapRefType(v.getType()))}; + bool followingData = !isBoxRef; mlir::SymbolRefAttr global; Source::Attributes attributes; while (defOp && !breakFromLoop) { @@ -282,34 +271,32 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) { v = op->getOperand(0); defOp = v.getDefiningOp(); if (mlir::isa(v.getType())) - followBoxAddr = true; + followBoxData = true; }) .Case([&](auto op) { v = op->getOperand(0); defOp = v.getDefiningOp(); if (mlir::isa(v.getType())) - followBoxAddr = true; + followBoxData = true; approximateSource = true; }) .Case([&](auto op) { - if (followBoxAddr) { + if (followBoxData) { v = op->getOperand(0); defOp = v.getDefiningOp(); } else breakFromLoop = true; }) .Case([&](auto op) { - if (followBoxAddr && mlir::isa(op.getType())) { - // For now, support the load of an argument or fir.address_of - // TODO: generalize to all operations (in particular fir.alloca and - // fir.allocmem) - auto def = getOriginalDef(op.getMemref()); - if (isDummyArgument(def) || - def.template getDefiningOp()) { - v = def; - defOp = v.getDefiningOp(); - return; - } + // If the load is from a leaf source, return the leaf. Do not track + // through indirections otherwise. + // TODO: Add support to fir.alloca and fir.allocmem + auto def = getOriginalDef(op.getMemref()); + if (isDummyArgument(def) || + def.template getDefiningOp()) { + v = def; + defOp = v.getDefiningOp(); + return; } // No further tracking for addresses loaded from memory for now. type = SourceKind::Indirect; @@ -318,24 +305,7 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) { .Case([&](auto op) { // Address of a global scope object. ty = v.getType(); - - // When the global is a - // fir.global @_QMpointersEp : !fir.box> - // or - // fir.global @_QMpointersEp : !fir.box> - // - // and when following through the wrapped address, capture - // the fact that there is nothing known about it. Therefore setting - // the source to Direct. - // - // When not following the wrapped address, then consider the address - // of the box, which has nothing to do with the wrapped address and - // lies in the global memory space. - if (followBoxAddr && - mlir::isa(fir::unwrapRefType(ty))) - type = SourceKind::Direct; - else - type = SourceKind::Global; + type = SourceKind::Global; auto globalOpName = mlir::OperationName( fir::GlobalOp::getOperationName(), defOp->getContext()); @@ -343,11 +313,10 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) { v, fir::GlobalOp::getTargetAttrName(globalOpName))) attributes.set(Attribute::Target); - // TODO: Take followBoxAddr into account when setting the pointer + // TODO: Take followBoxData into account when setting the pointer // attribute if (Source::isPointerReference(ty)) attributes.set(Attribute::Pointer); - global = llvm::cast(op).getSymbol(); breakFromLoop = true; }) @@ -393,7 +362,7 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) { // MustAlias after going through a designate operation approximateSource = true; if (mlir::isa(v.getType())) - followBoxAddr = true; + followBoxData = true; }) .Default([&](auto op) { defOp = nullptr; @@ -412,10 +381,10 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v) { attributes.set(Attribute::Pointer); } - if (type == SourceKind::Global || type == SourceKind::Direct) - return {global, type, ty, attributes, approximateSource}; - - return {v, type, ty, attributes, approximateSource}; + if (type == SourceKind::Global) { + return {{global, followingData}, type, ty, attributes, approximateSource}; + } + return {{v, followingData}, type, ty, attributes, approximateSource}; } } // namespace fir diff --git a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp index 3642a812096db4..caece93840043d 100644 --- a/flang/lib/Optimizer/Transforms/AddAliasTags.cpp +++ b/flang/lib/Optimizer/Transforms/AddAliasTags.cpp @@ -145,7 +145,7 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op, source.kind == fir::AliasAnalysis::SourceKind::Argument) { LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to dummy argument at " << *op << "\n"); - std::string name = getFuncArgName(source.u.get()); + std::string name = getFuncArgName(source.origin.u.get()); if (!name.empty()) tag = state.getFuncTree(func).dummyArgDataTree.getTag(name); else @@ -155,8 +155,9 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op, // TBAA for global variables } else if (enableGlobals && - source.kind == fir::AliasAnalysis::SourceKind::Global) { - mlir::SymbolRefAttr glbl = source.u.get(); + source.kind == fir::AliasAnalysis::SourceKind::Global && + !source.isBoxData()) { + mlir::SymbolRefAttr glbl = source.origin.u.get(); const char *name = glbl.getRootReference().data(); LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to global " << name << " at " << *op << "\n"); @@ -164,9 +165,10 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op, // TBAA for SourceKind::Direct } else if (enableDirect && - source.kind == fir::AliasAnalysis::SourceKind::Direct) { - if (source.u.is()) { - mlir::SymbolRefAttr glbl = source.u.get(); + source.kind == fir::AliasAnalysis::SourceKind::Global && + source.isBoxData()) { + if (source.origin.u.is()) { + mlir::SymbolRefAttr glbl = source.origin.u.get(); const char *name = glbl.getRootReference().data(); LLVM_DEBUG(llvm::dbgs().indent(2) << "Found reference to direct " << name << " at " << *op << "\n"); @@ -182,7 +184,8 @@ void AddAliasTagsPass::runOnAliasInterface(fir::FirAliasTagOpInterface op, } else if (enableLocalAllocs && source.kind == fir::AliasAnalysis::SourceKind::Allocate) { std::optional name; - mlir::Operation *sourceOp = source.u.get().getDefiningOp(); + mlir::Operation *sourceOp = + source.origin.u.get().getDefiningOp(); if (auto alloc = mlir::dyn_cast_or_null(sourceOp)) name = alloc.getUniqName(); else if (auto alloc = mlir::dyn_cast_or_null(sourceOp)) diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir index 31459ef21d947c..d03348efd2a68c 100644 --- a/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir +++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-2.fir @@ -37,14 +37,14 @@ // 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 +// CHECK-DAG: arg2.load#0 <-> func.region0#0: MayAlias +// CHECK-DAG: arg2.load#0 <-> func.region0#1: 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: MustAlias +// CHECK-DAG: arg2.load#0 <-> arg2.addr#0: MustAlias // 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"} { @@ -80,7 +80,7 @@ func.func @_QFPtest(%arg0: !fir.ref {fir.bindc_name = "v1", fir.target}, %a %14 = fir.box_addr %13 : (!fir.box>) -> !fir.ptr fir.store %14 to %4 : !fir.ref> - %15 = fir.load %arg2 : !fir.ref>> + %15 = fir.load %arg2 {test.ptr = "arg2.load"} : !fir.ref>> %16 = fir.box_addr %15 {test.ptr = "arg2.addr"} : (!fir.box>) -> !fir.ptr return } @@ -99,10 +99,12 @@ func.func @_QFPtest(%arg0: !fir.ref {fir.bindc_name = "v1", fir.target}, %a // 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 +// CHECK-DAG: arg1.load#0 <-> func.region0#0: MayAlias +// CHECK-DAG: arg2.load#0 <-> func.region0#0: MayAlias func.func @_QFPtest2(%arg0: !fir.ref {fir.bindc_name = "v1", fir.target}, %arg1: !fir.ref>>, %arg2: !fir.ref>> ) attributes {test.ptr = "func"} { + %0 = fir.load %arg1 {test.ptr = "arg1.load"} : !fir.ref>> + %1 = fir.load %arg2 {test.ptr = "arg2.load"} : !fir.ref>> return } @@ -141,14 +143,14 @@ func.func @_QFPtest2(%arg0: !fir.ref {fir.bindc_name = "v1", fir.target}, % // alias with the wrapped scalar _QFEvar2. We meant box_addr of _QMpointersEp // CHECK-DAG: p#0 <-> box.addr#0: NoAlias -// TODO: Still need to handle more gracefully the difference between !fir.ref> and !fir.box<> -// CHECK-DAG: box.addr#0 <-> func.region0#0: MayAlias +// Handling gracefully the difference between !fir.ref> and !fir.box<> +// CHECK-DAG: box.addr#0 <-> func.region0#0: NoAlias // 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 +// CHECK-DAG: arg0.load#0 <-> var2#0: MayAlias // It is the same as box.addr // CHECK-DAG: var2#0 <-> box.addr#0: MustAlias @@ -173,6 +175,7 @@ fir.global internal @_QFEvar2 target : f32 { } func.func @_QFPtest3(%arg0: !fir.ref>> {fir.bindc_name = "p1"}, %arg1: !fir.ref) attributes {test.ptr = "func"} { + %3 = fir.load %arg0 {test.ptr = "arg0.load"}: !fir.ref>> %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> @@ -214,10 +217,17 @@ func.func @_QFPtest3(%arg0: !fir.ref>> {fir.bindc_name = // CHECK-DAG: var2_hlfir#1 <-> p_hlfir#0: NoAlias // CHECK-DAG: var2_hlfir#1 <-> p_hlfir#1: NoAlias -// CHECK-DAG: var2#0 <-> func.region0#0: MayAlias -// CHECK-DAG: var2_fir#0 <-> func.region0#0: MayAlias -// CHECK-DAG: var2_hlfir#0 <-> func.region0#0: MayAlias -// CHECK-DAG: var2_hlfir#1 <-> func.region0#0: MayAlias +// The data cannot alias with the box references +// CHECK-DAG: var2#0 <-> func.region0#0: NoAlias +// CHECK-DAG: var2_fir#0 <-> func.region0#0: NoAlias +// CHECK-DAG: var2_hlfir#0 <-> func.region0#0: NoAlias +// CHECK-DAG: var2_hlfir#1 <-> func.region0#0: NoAlias + +// But it can alias with the box's own data +// CHECK-DAG: arg0.load#0 <-> var2#0: MayAlias +// CHECK-DAG: arg0.load#0 <-> var2_fir#0: MayAlias +// CHECK-DAG: arg0.load#0 <-> var2_hlfir#0: MayAlias +// CHECK-DAG: arg0.load#0 <-> var2_hlfir#1: MayAlias // CHECK-DAG: var2#0 <-> box.addr#0: MustAlias // CHECK-DAG: var2#0 <-> box.addr_fir#0: MustAlias @@ -255,6 +265,7 @@ fir.global internal @_QFEvar2 target : f32 { } func.func @_QFPtest4(%arg0: !fir.ref>> {fir.bindc_name = "p1"}, %arg1: !fir.ref) attributes {test.ptr = "func"} { + %3 = fir.load %arg0 {test.ptr = "arg0.load"} : !fir.ref>> %4 = fir.address_of(@_QFEvar2) {test.ptr = "var2"} : !fir.ref %fir_decl_var2 = fir.declare %4 {uniq_name = "var2_fir", test.ptr = "var2_fir"}: (!fir.ref) -> !fir.ref %hlfir_decl_var2:2 = hlfir.declare %4 {uniq_name = "var2_hlfir", test.ptr = "var2_hlfir"}: (!fir.ref) -> (!fir.ref, !fir.ref) diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir index d7bd970b51907f..91829a461dc72a 100644 --- a/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir +++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-3.fir @@ -23,8 +23,8 @@ // 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 +// pointer_dummy's box cannot alias with raw reference to f32 (x) +// CHECK: func.region0#0 <-> func.region0#1: NoAlias fir.global @_QMmEa : !fir.type<_QMmTt{pointer_component:!fir.box>}> { %0 = fir.undefined !fir.type<_QMmTt{pointer_component:!fir.box>}> diff --git a/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir new file mode 100644 index 00000000000000..df24a6d32aa596 --- /dev/null +++ b/flang/test/Analysis/AliasAnalysis/alias-analysis-9.fir @@ -0,0 +1,59 @@ +// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' 2>&1 | FileCheck %s + + +// module m +// type t +// type(t), pointer :: next +// integer :: i +// end type +// contains +// subroutine foo(x, y) +// type(t) :: x, y +// integer :: i1, i2 +// i1 = x%next%i +// x = y +// i2 = x%next%i +// end subroutine +// 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 + +// 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 + +// 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 + +// 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 + +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"} + %1:2 = hlfir.declare %0 {uniq_name = "_QMmFfooEi1"} : (!fir.ref) -> (!fir.ref, !fir.ref) + %2 = fir.alloca i32 {bindc_name = "i2", uniq_name = "_QMmFfooEi2"} + %3:2 = hlfir.declare %2 {uniq_name = "_QMmFfooEi2"} : (!fir.ref) -> (!fir.ref, !fir.ref) + %4:2 = hlfir.declare %arg0 {uniq_name = "_QMmFfooEx", test.ptr = "x"} : (!fir.ref>>,i:i32}>>) -> (!fir.ref>>,i:i32}>>, !fir.ref>>,i:i32}>>) + %5:2 = hlfir.declare %arg1 {uniq_name = "_QMmFfooEy", test.ptr = "y"} : (!fir.ref>>,i:i32}>>) -> (!fir.ref>>,i:i32}>>, !fir.ref>>,i:i32}>>) + %6 = hlfir.designate %4#0{"next"} {fortran_attrs = #fir.var_attrs, test.ptr = "xnext1"} : (!fir.ref>>,i:i32}>>) -> !fir.ref>>,i:i32}>>>> + %7 = fir.load %6 : !fir.ref>>,i:i32}>>>> + %8 = fir.box_addr %7 : (!fir.box>>,i:i32}>>>) -> !fir.ptr>>,i:i32}>> + %9 = hlfir.designate %8{"i"} : (!fir.ptr>>,i:i32}>>) -> !fir.ref + %10 = fir.load %9 : !fir.ref + hlfir.assign %10 to %1#0 : i32, !fir.ref + hlfir.assign %5#0 to %4#0 : !fir.ref>>,i:i32}>>, !fir.ref>>,i:i32}>> + %11 = hlfir.designate %4#0{"next"} {fortran_attrs = #fir.var_attrs, test.ptr = "xnext2"} : (!fir.ref>>,i:i32}>>) -> !fir.ref>>,i:i32}>>>> + %12 = fir.load %11 : !fir.ref>>,i:i32}>>>> + %13 = fir.box_addr %12 : (!fir.box>>,i:i32}>>>) -> !fir.ptr>>,i:i32}>> + %14 = hlfir.designate %13{"i"} : (!fir.ptr>>,i:i32}>>) -> !fir.ref + %15 = fir.load %14 : !fir.ref + hlfir.assign %15 to %3#0 : i32, !fir.ref + return +}