855 lines · cpp
1//===- AliasAnalysis.cpp - Alias Analysis for FIR ------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "flang/Optimizer/Analysis/AliasAnalysis.h"10#include "flang/Optimizer/Dialect/FIROps.h"11#include "flang/Optimizer/Dialect/FIROpsSupport.h"12#include "flang/Optimizer/Dialect/FIRType.h"13#include "flang/Optimizer/Dialect/FortranVariableInterface.h"14#include "flang/Optimizer/HLFIR/HLFIROps.h"15#include "flang/Optimizer/Support/InternalNames.h"16#include "mlir/Analysis/AliasAnalysis.h"17#include "mlir/Dialect/OpenMP/OpenMPDialect.h"18#include "mlir/Dialect/OpenMP/OpenMPInterfaces.h"19#include "mlir/IR/BuiltinOps.h"20#include "mlir/IR/Value.h"21#include "mlir/Interfaces/SideEffectInterfaces.h"22#include "llvm/ADT/TypeSwitch.h"23#include "llvm/Support/Casting.h"24#include "llvm/Support/Debug.h"25 26using namespace mlir;27 28#define DEBUG_TYPE "fir-alias-analysis"29 30// Inspect for value-scoped Allocate effects and determine whether31// 'candidate' is a new allocation. Returns SourceKind::Allocate if a32// MemAlloc effect is attached33static fir::AliasAnalysis::SourceKind34classifyAllocateFromEffects(mlir::Operation *op, mlir::Value candidate) {35 if (!op)36 return fir::AliasAnalysis::SourceKind::Unknown;37 auto interface = llvm::dyn_cast<mlir::MemoryEffectOpInterface>(op);38 if (!interface)39 return fir::AliasAnalysis::SourceKind::Unknown;40 llvm::SmallVector<mlir::MemoryEffects::EffectInstance, 4> effects;41 interface.getEffects(effects);42 for (mlir::MemoryEffects::EffectInstance &e : effects) {43 if (mlir::isa<mlir::MemoryEffects::Allocate>(e.getEffect()) &&44 e.getValue() && e.getValue() == candidate)45 return fir::AliasAnalysis::SourceKind::Allocate;46 }47 return fir::AliasAnalysis::SourceKind::Unknown;48}49 50//===----------------------------------------------------------------------===//51// AliasAnalysis: alias52//===----------------------------------------------------------------------===//53 54static fir::AliasAnalysis::Source::Attributes55getAttrsFromVariable(fir::FortranVariableOpInterface var) {56 fir::AliasAnalysis::Source::Attributes attrs;57 if (var.isTarget())58 attrs.set(fir::AliasAnalysis::Attribute::Target);59 if (var.isPointer())60 attrs.set(fir::AliasAnalysis::Attribute::Pointer);61 if (var.isIntentIn())62 attrs.set(fir::AliasAnalysis::Attribute::IntentIn);63 64 return attrs;65}66 67static bool hasGlobalOpTargetAttr(mlir::Value v, fir::AddrOfOp op) {68 auto globalOpName =69 mlir::OperationName(fir::GlobalOp::getOperationName(), op->getContext());70 return fir::valueHasFirAttribute(71 v, fir::GlobalOp::getTargetAttrName(globalOpName));72}73 74static bool isEvaluateInMemoryBlockArg(mlir::Value v) {75 if (auto evalInMem = llvm::dyn_cast_or_null<hlfir::EvaluateInMemoryOp>(76 v.getParentRegion()->getParentOp()))77 return evalInMem.getMemory() == v;78 return false;79}80 81template <typename OMPTypeOp, typename DeclTypeOp>82static bool isPrivateArg(omp::BlockArgOpenMPOpInterface &argIface,83 OMPTypeOp &op, DeclTypeOp &declOp) {84 if (!op.getPrivateSyms().has_value())85 return false;86 for (auto [opSym, blockArg] :87 llvm::zip_equal(*op.getPrivateSyms(), argIface.getPrivateBlockArgs())) {88 if (blockArg == declOp.getMemref()) {89 return true;90 }91 }92 return false;93}94 95namespace fir {96 97void AliasAnalysis::Source::print(llvm::raw_ostream &os) const {98 if (auto v = llvm::dyn_cast<mlir::Value>(origin.u))99 os << v;100 else if (auto gbl = llvm::dyn_cast<mlir::SymbolRefAttr>(origin.u))101 os << gbl;102 os << " SourceKind: " << EnumToString(kind);103 os << " Type: " << valueType << " ";104 if (origin.isData) {105 os << " following data ";106 } else {107 os << " following box reference ";108 }109 attributes.Dump(os, EnumToString);110}111 112bool AliasAnalysis::isRecordWithPointerComponent(mlir::Type ty) {113 auto eleTy = fir::dyn_cast_ptrEleTy(ty);114 if (!eleTy)115 return false;116 // TO DO: Look for pointer components117 return mlir::isa<fir::RecordType>(eleTy);118}119 120bool AliasAnalysis::isPointerReference(mlir::Type ty) {121 auto eleTy = fir::dyn_cast_ptrEleTy(ty);122 if (!eleTy)123 return false;124 125 return fir::isPointerType(eleTy) || mlir::isa<fir::PointerType>(eleTy);126}127 128bool AliasAnalysis::Source::isTargetOrPointer() const {129 return attributes.test(Attribute::Pointer) ||130 attributes.test(Attribute::Target);131}132 133bool AliasAnalysis::Source::isTarget() const {134 return attributes.test(Attribute::Target);135}136 137bool AliasAnalysis::Source::isPointer() const {138 return attributes.test(Attribute::Pointer);139}140 141bool AliasAnalysis::Source::isDummyArgument() const {142 if (auto v = origin.u.dyn_cast<mlir::Value>()) {143 return fir::isDummyArgument(v);144 }145 return false;146}147 148bool AliasAnalysis::Source::isData() const { return origin.isData; }149bool AliasAnalysis::Source::isBoxData() const {150 return mlir::isa<fir::BaseBoxType>(fir::unwrapRefType(valueType)) &&151 origin.isData;152}153 154bool AliasAnalysis::Source::isFortranUserVariable() const {155 if (!origin.instantiationPoint)156 return false;157 return llvm::TypeSwitch<mlir::Operation *, bool>(origin.instantiationPoint)158 .template Case<fir::DeclareOp, hlfir::DeclareOp>([&](auto declOp) {159 return fir::NameUniquer::deconstruct(declOp.getUniqName()).first ==160 fir::NameUniquer::NameKind::VARIABLE;161 })162 .Default([&](auto op) { return false; });163}164 165bool AliasAnalysis::Source::mayBeDummyArgOrHostAssoc() const {166 return kind != SourceKind::Allocate && kind != SourceKind::Global;167}168 169bool AliasAnalysis::Source::mayBePtrDummyArgOrHostAssoc() const {170 // Must alias like dummy arg (or HostAssoc).171 if (!mayBeDummyArgOrHostAssoc())172 return false;173 // Must be address of the dummy arg not of a dummy arg component.174 if (isRecordWithPointerComponent(valueType))175 return false;176 // Must be address *of* (not *in*) a pointer.177 return attributes.test(Attribute::Pointer) && !isData();178}179 180bool AliasAnalysis::Source::mayBeActualArg() const {181 return kind != SourceKind::Allocate;182}183 184bool AliasAnalysis::Source::mayBeActualArgWithPtr(185 const mlir::Value *val) const {186 // Must not be local.187 if (!mayBeActualArg())188 return false;189 // Can be address *of* (not *in*) a pointer.190 if (attributes.test(Attribute::Pointer) && !isData())191 return true;192 // Can be address of a composite with a pointer component.193 if (isRecordWithPointerComponent(val->getType()))194 return true;195 return false;196}197 198AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {199 // A wrapper around alias(Source lhsSrc, Source rhsSrc, mlir::Value lhs,200 // mlir::Value rhs) This allows a user to provide Source that may be obtained201 // through other dialects202 auto lhsSrc = getSource(lhs);203 auto rhsSrc = getSource(rhs);204 return alias(lhsSrc, rhsSrc, lhs, rhs);205}206 207AliasResult AliasAnalysis::alias(Source lhsSrc, Source rhsSrc, mlir::Value lhs,208 mlir::Value rhs) {209 // TODO: alias() has to be aware of the function scopes.210 // After MLIR inlining, the current implementation may211 // not recognize non-aliasing entities.212 bool approximateSource = lhsSrc.approximateSource || rhsSrc.approximateSource;213 LLVM_DEBUG(llvm::dbgs() << "\nAliasAnalysis::alias\n";214 llvm::dbgs() << " lhs: " << lhs << "\n";215 llvm::dbgs() << " lhsSrc: " << lhsSrc << "\n";216 llvm::dbgs() << " rhs: " << rhs << "\n";217 llvm::dbgs() << " rhsSrc: " << rhsSrc << "\n";);218 219 // Indirect case currently not handled. Conservatively assume220 // it aliases with everything221 if (lhsSrc.kind >= SourceKind::Indirect ||222 rhsSrc.kind >= SourceKind::Indirect) {223 LLVM_DEBUG(llvm::dbgs() << " aliasing because of indirect access\n");224 return AliasResult::MayAlias;225 }226 227 if (lhsSrc.kind == rhsSrc.kind) {228 // If the kinds and origins are the same, then lhs and rhs must alias unless229 // either source is approximate. Approximate sources are for parts of the230 // origin, but we don't have info here on which parts and whether they231 // overlap, so we normally return MayAlias in that case.232 if (lhsSrc.origin == rhsSrc.origin) {233 LLVM_DEBUG(llvm::dbgs()234 << " aliasing because same source kind and origin\n");235 if (approximateSource)236 return AliasResult::MayAlias;237 // One should be careful about relying on MustAlias.238 // The LLVM definition implies that the two MustAlias239 // memory objects start at exactly the same location.240 // With Fortran array slices two objects may have241 // the same starting location, but otherwise represent242 // partially overlapping memory locations, e.g.:243 // integer :: a(10)244 // ... a(5:1:-1) ! starts at a(5) and addresses a(5), ..., a(1)245 // ... a(5:10:1) ! starts at a(5) and addresses a(5), ..., a(10)246 // The current implementation of FIR alias analysis will always247 // return MayAlias for such cases.248 return AliasResult::MustAlias;249 }250 // If one value is the address of a composite, and if the other value is the251 // address of a pointer/allocatable component of that composite, their252 // origins compare unequal because the latter has !isData(). As for the253 // address of any component vs. the address of the composite, a store to one254 // can affect a load from the other, so the result should be MayAlias. To255 // catch this case, we conservatively return MayAlias when one value is the256 // address of a composite, the other value is non-data, and they have the257 // same origin value.258 //259 // TODO: That logic does not check that the latter is actually a component260 // of the former, so it can return MayAlias when unnecessary. For example,261 // they might both be addresses of components of a larger composite.262 //263 // FIXME: Actually, we should generalize from isRecordWithPointerComponent264 // to any composite because a component with !isData() is not always a265 // pointer. However, Source::isRecordWithPointerComponent currently doesn't266 // actually check for pointer components, so it's fine for now.267 if (lhsSrc.origin.u == rhsSrc.origin.u &&268 ((isRecordWithPointerComponent(lhs.getType()) && !rhsSrc.isData()) ||269 (isRecordWithPointerComponent(rhs.getType()) && !lhsSrc.isData()))) {270 LLVM_DEBUG(llvm::dbgs()271 << " aliasing between composite and non-data component with "272 << "same source kind and origin value\n");273 return AliasResult::MayAlias;274 }275 276 // Two host associated accesses may overlap due to an equivalence.277 if (lhsSrc.kind == SourceKind::HostAssoc) {278 LLVM_DEBUG(llvm::dbgs() << " aliasing because of host association\n");279 return AliasResult::MayAlias;280 }281 }282 283 Source *src1, *src2;284 mlir::Value *val1, *val2;285 if (lhsSrc.kind < rhsSrc.kind) {286 src1 = &lhsSrc;287 src2 = &rhsSrc;288 val1 = &lhs;289 val2 = &rhs;290 } else {291 src1 = &rhsSrc;292 src2 = &lhsSrc;293 val1 = &rhs;294 val2 = &lhs;295 }296 297 if (src1->kind == SourceKind::Argument &&298 src2->kind == SourceKind::HostAssoc) {299 // Treat the host entity as TARGET for the purpose of disambiguating300 // it with a dummy access. It is required for this particular case:301 // subroutine test302 // integer :: x(10)303 // call inner(x)304 // contains305 // subroutine inner(y)306 // integer, target :: y(:)307 // x(1) = y(1)308 // end subroutine inner309 // end subroutine test310 //311 // F18 15.5.2.13 (4) (b) allows 'x' and 'y' to address the same object.312 // 'y' has an explicit TARGET attribute, but 'x' has neither TARGET313 // nor POINTER.314 src2->attributes.set(Attribute::Target);315 }316 317 // Two TARGET/POINTERs may alias. The logic here focuses on data. Handling318 // of non-data is included below.319 if (src1->isTargetOrPointer() && src2->isTargetOrPointer() &&320 src1->isData() && src2->isData()) {321 LLVM_DEBUG(llvm::dbgs() << " aliasing because of target or pointer\n");322 return AliasResult::MayAlias;323 }324 325 // Aliasing for dummy arg with target attribute.326 //327 // The address of a dummy arg (or HostAssoc) may alias the address of a328 // non-local (global or another dummy arg) when both have target attributes.329 // If either is a composite, addresses of components may alias as well.330 //331 // The previous "if" calling isTargetOrPointer casts a very wide net and so332 // reports MayAlias for many such cases that would otherwise be reported here.333 // It specifically skips such cases where one or both values have !isData()334 // (e.g., address *of* pointer/allocatable component vs. address of335 // composite), so this "if" catches those cases.336 if (src1->attributes.test(Attribute::Target) &&337 src2->attributes.test(Attribute::Target) &&338 ((src1->mayBeDummyArgOrHostAssoc() && src2->mayBeActualArg()) ||339 (src2->mayBeDummyArgOrHostAssoc() && src1->mayBeActualArg()))) {340 LLVM_DEBUG(llvm::dbgs()341 << " aliasing between targets where one is a dummy arg\n");342 return AliasResult::MayAlias;343 }344 345 // Aliasing for dummy arg that is a pointer.346 //347 // The address of a pointer dummy arg (but not a pointer component of a dummy348 // arg) may alias the address of either (1) a non-local pointer or (2) thus a349 // non-local composite with a pointer component. A non-local might be a350 // global or another dummy arg. The following is an example of the global351 // composite case:352 //353 // module m354 // type t355 // real, pointer :: p356 // end type357 // type(t) :: a358 // type(t) :: b359 // contains360 // subroutine test(p)361 // real, pointer :: p362 // p = 42363 // a = b364 // print *, p365 // end subroutine366 // end module367 // program main368 // use m369 // real, target :: x1 = 1370 // real, target :: x2 = 2371 // a%p => x1372 // b%p => x2373 // call test(a%p)374 // end375 //376 // The dummy argument p is an alias for a%p, even for the purposes of pointer377 // association during the assignment a = b. Thus, the program should print 2.378 //379 // The same is true when p is HostAssoc. For example, we might replace the380 // test subroutine above with:381 //382 // subroutine test(p)383 // real, pointer :: p384 // call internal()385 // contains386 // subroutine internal()387 // p = 42388 // a = b389 // print *, p390 // end subroutine391 // end subroutine392 if ((src1->mayBePtrDummyArgOrHostAssoc() &&393 src2->mayBeActualArgWithPtr(val2)) ||394 (src2->mayBePtrDummyArgOrHostAssoc() &&395 src1->mayBeActualArgWithPtr(val1))) {396 LLVM_DEBUG(llvm::dbgs()397 << " aliasing between pointer dummy arg and either pointer or "398 << "composite with pointer component\n");399 return AliasResult::MayAlias;400 }401 402 return AliasResult::NoAlias;403}404 405//===----------------------------------------------------------------------===//406// AliasAnalysis: getModRef407//===----------------------------------------------------------------------===//408 409static bool isSavedLocal(const fir::AliasAnalysis::Source &src) {410 if (auto symRef = llvm::dyn_cast<mlir::SymbolRefAttr>(src.origin.u)) {411 auto [nameKind, deconstruct] =412 fir::NameUniquer::deconstruct(symRef.getLeafReference().getValue());413 return nameKind == fir::NameUniquer::NameKind::VARIABLE &&414 !deconstruct.procs.empty();415 }416 return false;417}418 419static bool isCallToFortranUserProcedure(fir::CallOp call) {420 // TODO: indirect calls are excluded by these checks. Maybe some attribute is421 // needed to flag user calls in this case.422 if (fir::hasBindcAttr(call))423 return true;424 if (std::optional<mlir::SymbolRefAttr> callee = call.getCallee())425 return fir::NameUniquer::deconstruct(callee->getLeafReference().getValue())426 .first == fir::NameUniquer::NameKind::PROCEDURE;427 return false;428}429 430static ModRefResult getCallModRef(fir::CallOp call, mlir::Value var) {431 // TODO: limit to Fortran functions??432 // 1. Detect variables that can be accessed indirectly.433 fir::AliasAnalysis aliasAnalysis;434 fir::AliasAnalysis::Source varSrc = aliasAnalysis.getSource(var);435 // If the variable is not a user variable, we cannot safely assume that436 // Fortran semantics apply (e.g., a bare alloca/allocmem result may very well437 // be placed in an allocatable/pointer descriptor and escape).438 439 // All the logic below is based on Fortran semantics and only holds if this440 // is a call to a procedure from the Fortran source and this is a variable441 // from the Fortran source. Compiler generated temporaries or functions may442 // not adhere to this semantic.443 // TODO: add some opt-in or op-out mechanism for compiler generated temps.444 // An example of something currently problematic is the allocmem generated for445 // ALLOCATE of allocatable target. It currently does not have the target446 // attribute, which would lead this analysis to believe it cannot escape.447 if (!varSrc.isFortranUserVariable() || !isCallToFortranUserProcedure(call))448 return ModRefResult::getModAndRef();449 // Pointer and target may have been captured.450 if (varSrc.isTargetOrPointer())451 return ModRefResult::getModAndRef();452 // Host associated variables may be addressed indirectly via an internal453 // function call, whether the call is in the parent or an internal procedure.454 // Note that the host associated/internal procedure may be referenced455 // indirectly inside calls to non internal procedure. This is because internal456 // procedures may be captured or passed. As this is tricky to analyze, always457 // consider such variables may be accessed in any calls.458 if (varSrc.kind == fir::AliasAnalysis::SourceKind::HostAssoc ||459 varSrc.isCapturedInInternalProcedure)460 return ModRefResult::getModAndRef();461 // At that stage, it has been ruled out that local (including the saved ones)462 // and dummy cannot be indirectly accessed in the call.463 if (varSrc.kind != fir::AliasAnalysis::SourceKind::Allocate &&464 !varSrc.isDummyArgument()) {465 if (varSrc.kind != fir::AliasAnalysis::SourceKind::Global ||466 !isSavedLocal(varSrc))467 return ModRefResult::getModAndRef();468 }469 // 2. Check if the variable is passed via the arguments.470 for (auto arg : call.getArgs()) {471 if (fir::conformsWithPassByRef(arg.getType()) &&472 !aliasAnalysis.alias(arg, var).isNo()) {473 // TODO: intent(in) would allow returning Ref here. This can be obtained474 // in the func.func attributes for direct calls, but the module lookup is475 // linear with the number of MLIR symbols, which would introduce a pseudo476 // quadratic behavior num_calls * num_func.477 return ModRefResult::getModAndRef();478 }479 }480 // The call cannot access the variable.481 return ModRefResult::getNoModRef();482}483 484/// This is mostly inspired by MLIR::LocalAliasAnalysis with 2 notable485/// differences 1) Regions are not handled here but will be handled by a data486/// flow analysis to come 2) Allocate and Free effects are considered487/// modifying488ModRefResult AliasAnalysis::getModRef(Operation *op, Value location) {489 MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op);490 if (!interface) {491 if (auto call = llvm::dyn_cast<fir::CallOp>(op))492 return getCallModRef(call, location);493 return ModRefResult::getModAndRef();494 }495 496 // Build a ModRefResult by merging the behavior of the effects of this497 // operation.498 SmallVector<MemoryEffects::EffectInstance> effects;499 interface.getEffects(effects);500 501 ModRefResult result = ModRefResult::getNoModRef();502 for (const MemoryEffects::EffectInstance &effect : effects) {503 504 // Check for an alias between the effect and our memory location.505 AliasResult aliasResult = AliasResult::MayAlias;506 if (Value effectValue = effect.getValue())507 aliasResult = alias(effectValue, location);508 509 // If we don't alias, ignore this effect.510 if (aliasResult.isNo())511 continue;512 513 // Merge in the corresponding mod or ref for this effect.514 if (isa<MemoryEffects::Read>(effect.getEffect()))515 result = result.merge(ModRefResult::getRef());516 else517 result = result.merge(ModRefResult::getMod());518 519 if (result.isModAndRef())520 break;521 }522 return result;523}524 525ModRefResult AliasAnalysis::getModRef(mlir::Region ®ion,526 mlir::Value location) {527 ModRefResult result = ModRefResult::getNoModRef();528 for (mlir::Operation &op : region.getOps()) {529 if (op.hasTrait<mlir::OpTrait::HasRecursiveMemoryEffects>()) {530 for (mlir::Region &subRegion : op.getRegions()) {531 result = result.merge(getModRef(subRegion, location));532 // Fast return is already mod and ref.533 if (result.isModAndRef())534 return result;535 }536 // In MLIR, RecursiveMemoryEffects can be combined with537 // MemoryEffectOpInterface to describe extra effects on top of the538 // effects of the nested operations. However, the presence of539 // RecursiveMemoryEffects and the absence of MemoryEffectOpInterface540 // implies the operation has no other memory effects than the one of its541 // nested operations.542 if (!mlir::isa<mlir::MemoryEffectOpInterface>(op))543 continue;544 }545 result = result.merge(getModRef(&op, location));546 if (result.isModAndRef())547 return result;548 }549 return result;550}551 552AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,553 bool getLastInstantiationPoint) {554 auto *defOp = v.getDefiningOp();555 SourceKind type{SourceKind::Unknown};556 mlir::Type ty;557 bool breakFromLoop{false};558 bool approximateSource{false};559 bool isCapturedInInternalProcedure{false};560 bool followBoxData{mlir::isa<fir::BaseBoxType>(v.getType())};561 bool isBoxRef{fir::isa_ref_type(v.getType()) &&562 mlir::isa<fir::BaseBoxType>(fir::unwrapRefType(v.getType()))};563 bool followingData = !isBoxRef;564 mlir::SymbolRefAttr global;565 Source::Attributes attributes;566 mlir::Operation *instantiationPoint{nullptr};567 while (defOp && !breakFromLoop) {568 // Value-scoped allocation detection via effects.569 if (classifyAllocateFromEffects(defOp, v) == SourceKind::Allocate) {570 type = SourceKind::Allocate;571 break;572 }573 // Operations may have multiple results, so we need to analyze574 // the result for which the source is queried.575 auto opResult = mlir::cast<OpResult>(v);576 assert(opResult.getOwner() == defOp && "v must be a result of defOp");577 ty = opResult.getType();578 llvm::TypeSwitch<Operation *>(defOp)579 .Case<hlfir::AsExprOp>([&](auto op) {580 // TODO: we should probably always report hlfir.as_expr581 // as a unique source, and let the codegen decide whether582 // to use the original buffer or create a copy.583 v = op.getVar();584 defOp = v.getDefiningOp();585 })586 .Case<hlfir::AssociateOp>([&](auto op) {587 assert(opResult != op.getMustFreeStrorageFlag() &&588 "MustFreeStorageFlag result is not an aliasing candidate");589 590 mlir::Value source = op.getSource();591 if (fir::isa_trivial(source.getType())) {592 // Trivial values will always use distinct temp memory,593 // so we can classify this as Allocate and stop.594 type = SourceKind::Allocate;595 breakFromLoop = true;596 } else {597 // AssociateOp may reuse the expression storage,598 // so we have to trace further.599 v = source;600 defOp = v.getDefiningOp();601 }602 })603 .Case<fir::PackArrayOp>([&](auto op) {604 // The packed array is not distinguishable from the original605 // array, so skip PackArrayOp and track further through606 // the array operand.607 v = op.getArray();608 defOp = v.getDefiningOp();609 approximateSource = true;610 })611 .Case<fir::LoadOp>([&](auto op) {612 // If load is inside target and it points to mapped item,613 // continue tracking.614 Operation *loadMemrefOp = op.getMemref().getDefiningOp();615 bool isDeclareOp =616 llvm::isa_and_present<fir::DeclareOp>(loadMemrefOp) ||617 llvm::isa_and_present<hlfir::DeclareOp>(loadMemrefOp);618 if (isDeclareOp &&619 llvm::isa<omp::TargetOp>(loadMemrefOp->getParentOp())) {620 v = op.getMemref();621 defOp = v.getDefiningOp();622 return;623 }624 625 // If we are loading a box reference, but following the data,626 // we gather the attributes of the box to populate the source627 // and stop tracking.628 if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(ty);629 boxTy && followingData) {630 631 if (mlir::isa<fir::PointerType>(boxTy.getEleTy()))632 attributes.set(Attribute::Pointer);633 634 auto boxSrc = getSource(op.getMemref());635 attributes |= boxSrc.attributes;636 approximateSource |= boxSrc.approximateSource;637 isCapturedInInternalProcedure |=638 boxSrc.isCapturedInInternalProcedure;639 640 global = llvm::dyn_cast<mlir::SymbolRefAttr>(boxSrc.origin.u);641 if (global) {642 type = SourceKind::Global;643 } else {644 auto def = llvm::cast<mlir::Value>(boxSrc.origin.u);645 bool classified = false;646 if (auto defDefOp = def.getDefiningOp()) {647 if (classifyAllocateFromEffects(defDefOp, def) ==648 SourceKind::Allocate) {649 v = def;650 defOp = defDefOp;651 type = SourceKind::Allocate;652 classified = true;653 }654 }655 if (!classified) {656 if (isDummyArgument(def)) {657 defOp = nullptr;658 v = def;659 } else {660 type = SourceKind::Indirect;661 }662 }663 }664 breakFromLoop = true;665 return;666 }667 // No further tracking for addresses loaded from memory for now.668 type = SourceKind::Indirect;669 breakFromLoop = true;670 })671 .Case<fir::AddrOfOp>([&](auto op) {672 // Address of a global scope object.673 ty = v.getType();674 type = SourceKind::Global;675 676 if (hasGlobalOpTargetAttr(v, op))677 attributes.set(Attribute::Target);678 679 // TODO: Take followBoxData into account when setting the pointer680 // attribute681 if (isPointerReference(ty))682 attributes.set(Attribute::Pointer);683 global = llvm::cast<fir::AddrOfOp>(op).getSymbol();684 breakFromLoop = true;685 })686 .Case<hlfir::DeclareOp, fir::DeclareOp>([&](auto op) {687 // The declare operations support FortranObjectViewOpInterface,688 // but their handling is more complex. Maybe we can find better689 // abstractions to handle them in a general fashion.690 bool isPrivateItem = false;691 if (omp::BlockArgOpenMPOpInterface argIface =692 dyn_cast<omp::BlockArgOpenMPOpInterface>(op->getParentOp())) {693 Value ompValArg;694 llvm::TypeSwitch<Operation *>(op->getParentOp())695 .template Case<omp::TargetOp>([&](auto targetOp) {696 // If declare operation is inside omp target region,697 // continue alias analysis outside the target region698 for (auto [opArg, blockArg] : llvm::zip_equal(699 targetOp.getMapVars(), argIface.getMapBlockArgs())) {700 if (blockArg == op.getMemref()) {701 omp::MapInfoOp mapInfo =702 llvm::cast<omp::MapInfoOp>(opArg.getDefiningOp());703 ompValArg = mapInfo.getVarPtr();704 return;705 }706 }707 // If given operation does not reflect mapping item,708 // check private clause709 isPrivateItem = isPrivateArg(argIface, targetOp, op);710 })711 .template Case<omp::DistributeOp, omp::ParallelOp,712 omp::SectionsOp, omp::SimdOp, omp::SingleOp,713 omp::TaskloopOp, omp::TaskOp, omp::WsloopOp>(714 [&](auto privateOp) {715 isPrivateItem = isPrivateArg(argIface, privateOp, op);716 });717 if (ompValArg) {718 v = ompValArg;719 defOp = ompValArg.getDefiningOp();720 return;721 }722 }723 auto varIf = llvm::cast<fir::FortranVariableOpInterface>(defOp);724 // While going through a declare operation collect725 // the variable attributes from it. Right now, some726 // of the attributes are duplicated, e.g. a TARGET dummy727 // argument has the target attribute both on its declare728 // operation and on the entry block argument.729 // In case of host associated use, the declare operation730 // is the only carrier of the variable attributes,731 // so we have to collect them here.732 attributes |= getAttrsFromVariable(varIf);733 isCapturedInInternalProcedure |=734 varIf.isCapturedInInternalProcedure();735 if (varIf.isHostAssoc()) {736 // Do not track past such DeclareOp, because it does not737 // currently provide any useful information. The host associated738 // access will end up dereferencing the host association tuple,739 // so we may as well stop right now.740 v = opResult;741 // TODO: if the host associated variable is a dummy argument742 // of the host, I think, we can treat it as SourceKind::Argument743 // for the purpose of alias analysis inside the internal procedure.744 type = SourceKind::HostAssoc;745 breakFromLoop = true;746 return;747 }748 if (getLastInstantiationPoint) {749 // Fetch only the innermost instantiation point.750 if (!instantiationPoint)751 instantiationPoint = op;752 753 if (op.getDummyScope()) {754 // Do not track past DeclareOp that has the dummy_scope755 // operand. This DeclareOp is known to represent756 // a dummy argument for some runtime instantiation757 // of a procedure.758 type = SourceKind::Argument;759 breakFromLoop = true;760 return;761 }762 } else {763 instantiationPoint = op;764 }765 if (isPrivateItem) {766 type = SourceKind::Allocate;767 breakFromLoop = true;768 return;769 }770 // TODO: Look for the fortran attributes present on the operation771 // Track further through the operand772 v = op.getMemref();773 defOp = v.getDefiningOp();774 })775 .Case<fir::FortranObjectViewOpInterface>([&](auto op) {776 // This case must be located after the cases for concrete777 // operations that support FortraObjectViewOpInterface,778 // so that their special handling kicks in.779 780 // fir.embox/rebox case: this is the only case where we check781 // for followBoxData.782 // TODO: it looks like we do not have LIT tests that fail783 // upon removal of the followBoxData code. We should come up784 // with a test or remove this code.785 if (!followBoxData &&786 (mlir::isa<fir::EmboxOp>(op) || mlir::isa<fir::ReboxOp>(op))) {787 breakFromLoop = true;788 return;789 }790 791 // Collect attributes from FortranVariableOpInterface operations.792 if (auto varIf =793 mlir::dyn_cast<fir::FortranVariableOpInterface>(defOp))794 attributes |= getAttrsFromVariable(varIf);795 // Set Pointer attribute based on the reference type.796 if (isPointerReference(ty))797 attributes.set(Attribute::Pointer);798 799 // Update v to point to the operand that represents the object800 // referenced by the operation's result.801 v = op.getViewSource(opResult);802 defOp = v.getDefiningOp();803 // If the input the resulting object references are offsetted,804 // then set approximateSource.805 auto offset = op.getViewOffset(opResult);806 if (!offset || *offset != 0)807 approximateSource = true;808 809 // If the source is a box, and the result is not a box,810 // then this is one of the box "unpacking" operations,811 // so we should set followBoxData.812 if (mlir::isa<fir::BaseBoxType>(v.getType()) &&813 !mlir::isa<fir::BaseBoxType>(ty))814 followBoxData = true;815 })816 .Default([&](auto op) {817 defOp = nullptr;818 breakFromLoop = true;819 });820 }821 if (!defOp && type == SourceKind::Unknown) {822 // Check if the memory source is coming through a dummy argument.823 if (isDummyArgument(v)) {824 type = SourceKind::Argument;825 ty = v.getType();826 if (fir::valueHasFirAttribute(v, fir::getTargetAttrName()))827 attributes.set(Attribute::Target);828 829 if (isPointerReference(ty))830 attributes.set(Attribute::Pointer);831 } else if (isEvaluateInMemoryBlockArg(v)) {832 // hlfir.eval_in_mem block operands is allocated by the operation.833 type = SourceKind::Allocate;834 ty = v.getType();835 }836 }837 838 if (type == SourceKind::Global) {839 return {{global, instantiationPoint, followingData},840 type,841 ty,842 attributes,843 approximateSource,844 isCapturedInInternalProcedure};845 }846 return {{v, instantiationPoint, followingData},847 type,848 ty,849 attributes,850 approximateSource,851 isCapturedInInternalProcedure};852}853 854} // namespace fir855