brintos

brintos / llvm-project-archived public Read only

0
0
Text · 33.5 KiB · 0e956d8 Raw
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 &region,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