xref: /llvm-project/flang/lib/Optimizer/Analysis/AliasAnalysis.cpp (revision 5236e3dac59e16630a3730c84c2d3d65970a6db3)
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-exception
6 //
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 
26 using namespace mlir;
27 
28 #define DEBUG_TYPE "fir-alias-analysis"
29 
30 //===----------------------------------------------------------------------===//
31 // AliasAnalysis: alias
32 //===----------------------------------------------------------------------===//
33 
34 /// Temporary function to skip through all the no op operations
35 /// TODO: Generalize support of fir.load
36 static mlir::Value getOriginalDef(mlir::Value v) {
37   mlir::Operation *defOp;
38   bool breakFromLoop = false;
39   while (!breakFromLoop && (defOp = v.getDefiningOp())) {
40     llvm::TypeSwitch<Operation *>(defOp)
41         .Case<fir::ConvertOp>([&](fir::ConvertOp op) { v = op.getValue(); })
42         .Case<fir::DeclareOp, hlfir::DeclareOp>(
43             [&](auto op) { v = op.getMemref(); })
44         .Default([&](auto op) { breakFromLoop = true; });
45   }
46   return v;
47 }
48 
49 namespace fir {
50 
51 void AliasAnalysis::Source::print(llvm::raw_ostream &os) const {
52   if (auto v = llvm::dyn_cast<mlir::Value>(origin.u))
53     os << v;
54   else if (auto gbl = llvm::dyn_cast<mlir::SymbolRefAttr>(origin.u))
55     os << gbl;
56   os << " SourceKind: " << EnumToString(kind);
57   os << " Type: " << valueType << " ";
58   if (origin.isData) {
59     os << " following data ";
60   } else {
61     os << " following box reference ";
62   }
63   attributes.Dump(os, EnumToString);
64 }
65 
66 bool AliasAnalysis::isRecordWithPointerComponent(mlir::Type ty) {
67   auto eleTy = fir::dyn_cast_ptrEleTy(ty);
68   if (!eleTy)
69     return false;
70   // TO DO: Look for pointer components
71   return mlir::isa<fir::RecordType>(eleTy);
72 }
73 
74 bool AliasAnalysis::isPointerReference(mlir::Type ty) {
75   auto eleTy = fir::dyn_cast_ptrEleTy(ty);
76   if (!eleTy)
77     return false;
78 
79   return fir::isPointerType(eleTy) || mlir::isa<fir::PointerType>(eleTy);
80 }
81 
82 bool AliasAnalysis::Source::isTargetOrPointer() const {
83   return attributes.test(Attribute::Pointer) ||
84          attributes.test(Attribute::Target);
85 }
86 
87 bool AliasAnalysis::Source::isDummyArgument() const {
88   if (auto v = origin.u.dyn_cast<mlir::Value>()) {
89     return fir::isDummyArgument(v);
90   }
91   return false;
92 }
93 
94 static bool isEvaluateInMemoryBlockArg(mlir::Value v) {
95   if (auto evalInMem = llvm::dyn_cast_or_null<hlfir::EvaluateInMemoryOp>(
96           v.getParentRegion()->getParentOp()))
97     return evalInMem.getMemory() == v;
98   return false;
99 }
100 
101 bool AliasAnalysis::Source::isData() const { return origin.isData; }
102 bool AliasAnalysis::Source::isBoxData() const {
103   return mlir::isa<fir::BaseBoxType>(fir::unwrapRefType(valueType)) &&
104          origin.isData;
105 }
106 
107 bool AliasAnalysis::Source::isFortranUserVariable() const {
108   if (!origin.instantiationPoint)
109     return false;
110   return llvm::TypeSwitch<mlir::Operation *, bool>(origin.instantiationPoint)
111       .template Case<fir::DeclareOp, hlfir::DeclareOp>([&](auto declOp) {
112         return fir::NameUniquer::deconstruct(declOp.getUniqName()).first ==
113                fir::NameUniquer::NameKind::VARIABLE;
114       })
115       .Default([&](auto op) { return false; });
116 }
117 
118 bool AliasAnalysis::Source::mayBeDummyArgOrHostAssoc() const {
119   return kind != SourceKind::Allocate && kind != SourceKind::Global;
120 }
121 
122 bool AliasAnalysis::Source::mayBePtrDummyArgOrHostAssoc() const {
123   // Must alias like dummy arg (or HostAssoc).
124   if (!mayBeDummyArgOrHostAssoc())
125     return false;
126   // Must be address of the dummy arg not of a dummy arg component.
127   if (isRecordWithPointerComponent(valueType))
128     return false;
129   // Must be address *of* (not *in*) a pointer.
130   return attributes.test(Attribute::Pointer) && !isData();
131 }
132 
133 bool AliasAnalysis::Source::mayBeActualArg() const {
134   return kind != SourceKind::Allocate;
135 }
136 
137 bool AliasAnalysis::Source::mayBeActualArgWithPtr(
138     const mlir::Value *val) const {
139   // Must not be local.
140   if (!mayBeActualArg())
141     return false;
142   // Can be address *of* (not *in*) a pointer.
143   if (attributes.test(Attribute::Pointer) && !isData())
144     return true;
145   // Can be address of a composite with a pointer component.
146   if (isRecordWithPointerComponent(val->getType()))
147     return true;
148   return false;
149 }
150 
151 AliasResult AliasAnalysis::alias(mlir::Value lhs, mlir::Value rhs) {
152   // A wrapper around alias(Source lhsSrc, Source rhsSrc, mlir::Value lhs,
153   // mlir::Value rhs) This allows a user to provide Source that may be obtained
154   // through other dialects
155   auto lhsSrc = getSource(lhs);
156   auto rhsSrc = getSource(rhs);
157   return alias(lhsSrc, rhsSrc, lhs, rhs);
158 }
159 
160 AliasResult AliasAnalysis::alias(Source lhsSrc, Source rhsSrc, mlir::Value lhs,
161                                  mlir::Value rhs) {
162   // TODO: alias() has to be aware of the function scopes.
163   // After MLIR inlining, the current implementation may
164   // not recognize non-aliasing entities.
165   bool approximateSource = lhsSrc.approximateSource || rhsSrc.approximateSource;
166   LLVM_DEBUG(llvm::dbgs() << "\nAliasAnalysis::alias\n";
167              llvm::dbgs() << "  lhs: " << lhs << "\n";
168              llvm::dbgs() << "  lhsSrc: " << lhsSrc << "\n";
169              llvm::dbgs() << "  rhs: " << rhs << "\n";
170              llvm::dbgs() << "  rhsSrc: " << rhsSrc << "\n";);
171 
172   // Indirect case currently not handled. Conservatively assume
173   // it aliases with everything
174   if (lhsSrc.kind >= SourceKind::Indirect ||
175       rhsSrc.kind >= SourceKind::Indirect) {
176     LLVM_DEBUG(llvm::dbgs() << "  aliasing because of indirect access\n");
177     return AliasResult::MayAlias;
178   }
179 
180   if (lhsSrc.kind == rhsSrc.kind) {
181     // If the kinds and origins are the same, then lhs and rhs must alias unless
182     // either source is approximate.  Approximate sources are for parts of the
183     // origin, but we don't have info here on which parts and whether they
184     // overlap, so we normally return MayAlias in that case.
185     if (lhsSrc.origin == rhsSrc.origin) {
186       LLVM_DEBUG(llvm::dbgs()
187                  << "  aliasing because same source kind and origin\n");
188       if (approximateSource)
189         return AliasResult::MayAlias;
190       return AliasResult::MustAlias;
191     }
192     // If one value is the address of a composite, and if the other value is the
193     // address of a pointer/allocatable component of that composite, their
194     // origins compare unequal because the latter has !isData().  As for the
195     // address of any component vs. the address of the composite, a store to one
196     // can affect a load from the other, so the result should be MayAlias.  To
197     // catch this case, we conservatively return MayAlias when one value is the
198     // address of a composite, the other value is non-data, and they have the
199     // same origin value.
200     //
201     // TODO: That logic does not check that the latter is actually a component
202     // of the former, so it can return MayAlias when unnecessary.  For example,
203     // they might both be addresses of components of a larger composite.
204     //
205     // FIXME: Actually, we should generalize from isRecordWithPointerComponent
206     // to any composite because a component with !isData() is not always a
207     // pointer.  However, Source::isRecordWithPointerComponent currently doesn't
208     // actually check for pointer components, so it's fine for now.
209     if (lhsSrc.origin.u == rhsSrc.origin.u &&
210         ((isRecordWithPointerComponent(lhs.getType()) && !rhsSrc.isData()) ||
211          (isRecordWithPointerComponent(rhs.getType()) && !lhsSrc.isData()))) {
212       LLVM_DEBUG(llvm::dbgs()
213                  << "  aliasing between composite and non-data component with "
214                  << "same source kind and origin value\n");
215       return AliasResult::MayAlias;
216     }
217 
218     // Two host associated accesses may overlap due to an equivalence.
219     if (lhsSrc.kind == SourceKind::HostAssoc) {
220       LLVM_DEBUG(llvm::dbgs() << "  aliasing because of host association\n");
221       return AliasResult::MayAlias;
222     }
223   }
224 
225   Source *src1, *src2;
226   mlir::Value *val1, *val2;
227   if (lhsSrc.kind < rhsSrc.kind) {
228     src1 = &lhsSrc;
229     src2 = &rhsSrc;
230     val1 = &lhs;
231     val2 = &rhs;
232   } else {
233     src1 = &rhsSrc;
234     src2 = &lhsSrc;
235     val1 = &rhs;
236     val2 = &lhs;
237   }
238 
239   if (src1->kind == SourceKind::Argument &&
240       src2->kind == SourceKind::HostAssoc) {
241     // Treat the host entity as TARGET for the purpose of disambiguating
242     // it with a dummy access. It is required for this particular case:
243     // subroutine test
244     //   integer :: x(10)
245     //   call inner(x)
246     // contains
247     //   subroutine inner(y)
248     //     integer, target :: y(:)
249     //     x(1) = y(1)
250     //   end subroutine inner
251     // end subroutine test
252     //
253     // F18 15.5.2.13 (4) (b) allows 'x' and 'y' to address the same object.
254     // 'y' has an explicit TARGET attribute, but 'x' has neither TARGET
255     // nor POINTER.
256     src2->attributes.set(Attribute::Target);
257   }
258 
259   // Two TARGET/POINTERs may alias.  The logic here focuses on data.  Handling
260   // of non-data is included below.
261   if (src1->isTargetOrPointer() && src2->isTargetOrPointer() &&
262       src1->isData() && src2->isData()) {
263     LLVM_DEBUG(llvm::dbgs() << "  aliasing because of target or pointer\n");
264     return AliasResult::MayAlias;
265   }
266 
267   // Aliasing for dummy arg with target attribute.
268   //
269   // The address of a dummy arg (or HostAssoc) may alias the address of a
270   // non-local (global or another dummy arg) when both have target attributes.
271   // If either is a composite, addresses of components may alias as well.
272   //
273   // The previous "if" calling isTargetOrPointer casts a very wide net and so
274   // reports MayAlias for many such cases that would otherwise be reported here.
275   // It specifically skips such cases where one or both values have !isData()
276   // (e.g., address *of* pointer/allocatable component vs. address of
277   // composite), so this "if" catches those cases.
278   if (src1->attributes.test(Attribute::Target) &&
279       src2->attributes.test(Attribute::Target) &&
280       ((src1->mayBeDummyArgOrHostAssoc() && src2->mayBeActualArg()) ||
281        (src2->mayBeDummyArgOrHostAssoc() && src1->mayBeActualArg()))) {
282     LLVM_DEBUG(llvm::dbgs()
283                << "  aliasing between targets where one is a dummy arg\n");
284     return AliasResult::MayAlias;
285   }
286 
287   // Aliasing for dummy arg that is a pointer.
288   //
289   // The address of a pointer dummy arg (but not a pointer component of a dummy
290   // arg) may alias the address of either (1) a non-local pointer or (2) thus a
291   // non-local composite with a pointer component.  A non-local might be a
292   // global or another dummy arg.  The following is an example of the global
293   // composite case:
294   //
295   // module m
296   //   type t
297   //      real, pointer :: p
298   //   end type
299   //   type(t) :: a
300   //   type(t) :: b
301   // contains
302   //   subroutine test(p)
303   //     real, pointer :: p
304   //     p = 42
305   //     a = b
306   //     print *, p
307   //   end subroutine
308   // end module
309   // program main
310   //   use m
311   //   real, target :: x1 = 1
312   //   real, target :: x2 = 2
313   //   a%p => x1
314   //   b%p => x2
315   //   call test(a%p)
316   // end
317   //
318   // The dummy argument p is an alias for a%p, even for the purposes of pointer
319   // association during the assignment a = b.  Thus, the program should print 2.
320   //
321   // The same is true when p is HostAssoc.  For example, we might replace the
322   // test subroutine above with:
323   //
324   // subroutine test(p)
325   //   real, pointer :: p
326   //   call internal()
327   // contains
328   //   subroutine internal()
329   //     p = 42
330   //     a = b
331   //     print *, p
332   //   end subroutine
333   // end subroutine
334   if ((src1->mayBePtrDummyArgOrHostAssoc() &&
335        src2->mayBeActualArgWithPtr(val2)) ||
336       (src2->mayBePtrDummyArgOrHostAssoc() &&
337        src1->mayBeActualArgWithPtr(val1))) {
338     LLVM_DEBUG(llvm::dbgs()
339                << "  aliasing between pointer dummy arg and either pointer or "
340                << "composite with pointer component\n");
341     return AliasResult::MayAlias;
342   }
343 
344   return AliasResult::NoAlias;
345 }
346 
347 //===----------------------------------------------------------------------===//
348 // AliasAnalysis: getModRef
349 //===----------------------------------------------------------------------===//
350 
351 static bool isSavedLocal(const fir::AliasAnalysis::Source &src) {
352   if (auto symRef = llvm::dyn_cast<mlir::SymbolRefAttr>(src.origin.u)) {
353     auto [nameKind, deconstruct] =
354         fir::NameUniquer::deconstruct(symRef.getLeafReference().getValue());
355     return nameKind == fir::NameUniquer::NameKind::VARIABLE &&
356            !deconstruct.procs.empty();
357   }
358   return false;
359 }
360 
361 static bool isCallToFortranUserProcedure(fir::CallOp call) {
362   // TODO: indirect calls are excluded by these checks. Maybe some attribute is
363   // needed to flag user calls in this case.
364   if (fir::hasBindcAttr(call))
365     return true;
366   if (std::optional<mlir::SymbolRefAttr> callee = call.getCallee())
367     return fir::NameUniquer::deconstruct(callee->getLeafReference().getValue())
368                .first == fir::NameUniquer::NameKind::PROCEDURE;
369   return false;
370 }
371 
372 static ModRefResult getCallModRef(fir::CallOp call, mlir::Value var) {
373   // TODO: limit to Fortran functions??
374   // 1. Detect variables that can be accessed indirectly.
375   fir::AliasAnalysis aliasAnalysis;
376   fir::AliasAnalysis::Source varSrc = aliasAnalysis.getSource(var);
377   // If the variable is not a user variable, we cannot safely assume that
378   // Fortran semantics apply (e.g., a bare alloca/allocmem result may very well
379   // be placed in an allocatable/pointer descriptor and escape).
380 
381   // All the logic below is based on Fortran semantics and only holds if this
382   // is a call to a procedure from the Fortran source and this is a variable
383   // from the Fortran source. Compiler generated temporaries or functions may
384   // not adhere to this semantic.
385   // TODO: add some opt-in or op-out mechanism for compiler generated temps.
386   // An example of something currently problematic is the allocmem generated for
387   // ALLOCATE of allocatable target. It currently does not have the target
388   // attribute, which would lead this analysis to believe it cannot escape.
389   if (!varSrc.isFortranUserVariable() || !isCallToFortranUserProcedure(call))
390     return ModRefResult::getModAndRef();
391   // Pointer and target may have been captured.
392   if (varSrc.isTargetOrPointer())
393     return ModRefResult::getModAndRef();
394   // Host associated variables may be addressed indirectly via an internal
395   // function call, whether the call is in the parent or an internal procedure.
396   // Note that the host associated/internal procedure may be referenced
397   // indirectly inside calls to non internal procedure. This is because internal
398   // procedures may be captured or passed. As this is tricky to analyze, always
399   // consider such variables may be accessed in any calls.
400   if (varSrc.kind == fir::AliasAnalysis::SourceKind::HostAssoc ||
401       varSrc.isCapturedInInternalProcedure)
402     return ModRefResult::getModAndRef();
403   // At that stage, it has been ruled out that local (including the saved ones)
404   // and dummy cannot be indirectly accessed in the call.
405   if (varSrc.kind != fir::AliasAnalysis::SourceKind::Allocate &&
406       !varSrc.isDummyArgument()) {
407     if (varSrc.kind != fir::AliasAnalysis::SourceKind::Global ||
408         !isSavedLocal(varSrc))
409       return ModRefResult::getModAndRef();
410   }
411   // 2. Check if the variable is passed via the arguments.
412   for (auto arg : call.getArgs()) {
413     if (fir::conformsWithPassByRef(arg.getType()) &&
414         !aliasAnalysis.alias(arg, var).isNo()) {
415       // TODO: intent(in) would allow returning Ref here. This can be obtained
416       // in the func.func attributes for direct calls, but the module lookup is
417       // linear with the number of MLIR symbols, which would introduce a pseudo
418       // quadratic behavior num_calls * num_func.
419       return ModRefResult::getModAndRef();
420     }
421   }
422   // The call cannot access the variable.
423   return ModRefResult::getNoModRef();
424 }
425 
426 /// This is mostly inspired by MLIR::LocalAliasAnalysis with 2 notable
427 /// differences 1) Regions are not handled here but will be handled by a data
428 /// flow analysis to come 2) Allocate and Free effects are considered
429 /// modifying
430 ModRefResult AliasAnalysis::getModRef(Operation *op, Value location) {
431   MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op);
432   if (!interface) {
433     if (auto call = llvm::dyn_cast<fir::CallOp>(op))
434       return getCallModRef(call, location);
435     return ModRefResult::getModAndRef();
436   }
437 
438   // Build a ModRefResult by merging the behavior of the effects of this
439   // operation.
440   SmallVector<MemoryEffects::EffectInstance> effects;
441   interface.getEffects(effects);
442 
443   ModRefResult result = ModRefResult::getNoModRef();
444   for (const MemoryEffects::EffectInstance &effect : effects) {
445 
446     // Check for an alias between the effect and our memory location.
447     AliasResult aliasResult = AliasResult::MayAlias;
448     if (Value effectValue = effect.getValue())
449       aliasResult = alias(effectValue, location);
450 
451     // If we don't alias, ignore this effect.
452     if (aliasResult.isNo())
453       continue;
454 
455     // Merge in the corresponding mod or ref for this effect.
456     if (isa<MemoryEffects::Read>(effect.getEffect()))
457       result = result.merge(ModRefResult::getRef());
458     else
459       result = result.merge(ModRefResult::getMod());
460 
461     if (result.isModAndRef())
462       break;
463   }
464   return result;
465 }
466 
467 ModRefResult AliasAnalysis::getModRef(mlir::Region &region,
468                                       mlir::Value location) {
469   ModRefResult result = ModRefResult::getNoModRef();
470   for (mlir::Operation &op : region.getOps()) {
471     if (op.hasTrait<mlir::OpTrait::HasRecursiveMemoryEffects>()) {
472       for (mlir::Region &subRegion : op.getRegions()) {
473         result = result.merge(getModRef(subRegion, location));
474         // Fast return is already mod and ref.
475         if (result.isModAndRef())
476           return result;
477       }
478       // In MLIR, RecursiveMemoryEffects can be combined with
479       // MemoryEffectOpInterface to describe extra effects on top of the
480       // effects of the nested operations.  However, the presence of
481       // RecursiveMemoryEffects and the absence of MemoryEffectOpInterface
482       // implies the operation has no other memory effects than the one of its
483       // nested operations.
484       if (!mlir::isa<mlir::MemoryEffectOpInterface>(op))
485         continue;
486     }
487     result = result.merge(getModRef(&op, location));
488     if (result.isModAndRef())
489       return result;
490   }
491   return result;
492 }
493 
494 AliasAnalysis::Source::Attributes
495 getAttrsFromVariable(fir::FortranVariableOpInterface var) {
496   AliasAnalysis::Source::Attributes attrs;
497   if (var.isTarget())
498     attrs.set(AliasAnalysis::Attribute::Target);
499   if (var.isPointer())
500     attrs.set(AliasAnalysis::Attribute::Pointer);
501   if (var.isIntentIn())
502     attrs.set(AliasAnalysis::Attribute::IntentIn);
503 
504   return attrs;
505 }
506 
507 template <typename OMPTypeOp, typename DeclTypeOp>
508 static bool isPrivateArg(omp::BlockArgOpenMPOpInterface &argIface,
509                          OMPTypeOp &op, DeclTypeOp &declOp) {
510   if (!op.getPrivateSyms().has_value())
511     return false;
512   for (auto [opSym, blockArg] :
513        llvm::zip_equal(*op.getPrivateSyms(), argIface.getPrivateBlockArgs())) {
514     if (blockArg == declOp.getMemref()) {
515       return true;
516     }
517   }
518   return false;
519 }
520 
521 AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
522                                                bool getLastInstantiationPoint) {
523   auto *defOp = v.getDefiningOp();
524   SourceKind type{SourceKind::Unknown};
525   mlir::Type ty;
526   bool breakFromLoop{false};
527   bool approximateSource{false};
528   bool isCapturedInInternalProcedure{false};
529   bool followBoxData{mlir::isa<fir::BaseBoxType>(v.getType())};
530   bool isBoxRef{fir::isa_ref_type(v.getType()) &&
531                 mlir::isa<fir::BaseBoxType>(fir::unwrapRefType(v.getType()))};
532   bool followingData = !isBoxRef;
533   mlir::SymbolRefAttr global;
534   Source::Attributes attributes;
535   mlir::Operation *instantiationPoint{nullptr};
536   while (defOp && !breakFromLoop) {
537     ty = defOp->getResultTypes()[0];
538     llvm::TypeSwitch<Operation *>(defOp)
539         .Case<hlfir::AsExprOp>([&](auto op) {
540           v = op.getVar();
541           defOp = v.getDefiningOp();
542         })
543         .Case<fir::AllocaOp, fir::AllocMemOp>([&](auto op) {
544           // Unique memory allocation.
545           type = SourceKind::Allocate;
546           breakFromLoop = true;
547         })
548         .Case<fir::ConvertOp>([&](auto op) {
549           // Skip ConvertOp's and track further through the operand.
550           v = op->getOperand(0);
551           defOp = v.getDefiningOp();
552         })
553         .Case<fir::BoxAddrOp>([&](auto op) {
554           v = op->getOperand(0);
555           defOp = v.getDefiningOp();
556           if (mlir::isa<fir::BaseBoxType>(v.getType()))
557             followBoxData = true;
558         })
559         .Case<fir::ArrayCoorOp, fir::CoordinateOp>([&](auto op) {
560           if (isPointerReference(ty))
561             attributes.set(Attribute::Pointer);
562           v = op->getOperand(0);
563           defOp = v.getDefiningOp();
564           if (mlir::isa<fir::BaseBoxType>(v.getType()))
565             followBoxData = true;
566           approximateSource = true;
567         })
568         .Case<fir::EmboxOp, fir::ReboxOp>([&](auto op) {
569           if (followBoxData) {
570             v = op->getOperand(0);
571             defOp = v.getDefiningOp();
572           } else
573             breakFromLoop = true;
574         })
575         .Case<fir::LoadOp>([&](auto op) {
576           // If the load is from a leaf source, return the leaf. Do not track
577           // through indirections otherwise.
578           // TODO: Add support to fir.alloca and fir.allocmem
579           auto def = getOriginalDef(op.getMemref());
580           if (isDummyArgument(def) ||
581               def.template getDefiningOp<fir::AddrOfOp>()) {
582             v = def;
583             defOp = v.getDefiningOp();
584             return;
585           }
586           // If load is inside target and it points to mapped item,
587           // continue tracking.
588           Operation *loadMemrefOp = op.getMemref().getDefiningOp();
589           bool isDeclareOp =
590               llvm::isa_and_present<fir::DeclareOp>(loadMemrefOp) ||
591               llvm::isa_and_present<hlfir::DeclareOp>(loadMemrefOp);
592           if (isDeclareOp &&
593               llvm::isa<omp::TargetOp>(loadMemrefOp->getParentOp())) {
594             v = op.getMemref();
595             defOp = v.getDefiningOp();
596             return;
597           }
598           // No further tracking for addresses loaded from memory for now.
599           type = SourceKind::Indirect;
600           breakFromLoop = true;
601         })
602         .Case<fir::AddrOfOp>([&](auto op) {
603           // Address of a global scope object.
604           ty = v.getType();
605           type = SourceKind::Global;
606 
607           auto globalOpName = mlir::OperationName(
608               fir::GlobalOp::getOperationName(), defOp->getContext());
609           if (fir::valueHasFirAttribute(
610                   v, fir::GlobalOp::getTargetAttrName(globalOpName)))
611             attributes.set(Attribute::Target);
612 
613           // TODO: Take followBoxData into account when setting the pointer
614           // attribute
615           if (isPointerReference(ty))
616             attributes.set(Attribute::Pointer);
617           global = llvm::cast<fir::AddrOfOp>(op).getSymbol();
618           breakFromLoop = true;
619         })
620         .Case<hlfir::DeclareOp, fir::DeclareOp>([&](auto op) {
621           bool isPrivateItem = false;
622           if (omp::BlockArgOpenMPOpInterface argIface =
623                   dyn_cast<omp::BlockArgOpenMPOpInterface>(op->getParentOp())) {
624             Value ompValArg;
625             llvm::TypeSwitch<Operation *>(op->getParentOp())
626                 .template Case<omp::TargetOp>([&](auto targetOp) {
627                   // If declare operation is inside omp target region,
628                   // continue alias analysis outside the target region
629                   for (auto [opArg, blockArg] : llvm::zip_equal(
630                            targetOp.getMapVars(), argIface.getMapBlockArgs())) {
631                     if (blockArg == op.getMemref()) {
632                       omp::MapInfoOp mapInfo =
633                           llvm::cast<omp::MapInfoOp>(opArg.getDefiningOp());
634                       ompValArg = mapInfo.getVarPtr();
635                       return;
636                     }
637                   }
638                   // If given operation does not reflect mapping item,
639                   // check private clause
640                   isPrivateItem = isPrivateArg(argIface, targetOp, op);
641                 })
642                 .template Case<omp::DistributeOp, omp::ParallelOp,
643                                omp::SectionsOp, omp::SimdOp, omp::SingleOp,
644                                omp::TaskloopOp, omp::TaskOp, omp::WsloopOp>(
645                     [&](auto privateOp) {
646                       isPrivateItem = isPrivateArg(argIface, privateOp, op);
647                     });
648             if (ompValArg) {
649               v = ompValArg;
650               defOp = ompValArg.getDefiningOp();
651               return;
652             }
653           }
654           auto varIf = llvm::cast<fir::FortranVariableOpInterface>(defOp);
655           // While going through a declare operation collect
656           // the variable attributes from it. Right now, some
657           // of the attributes are duplicated, e.g. a TARGET dummy
658           // argument has the target attribute both on its declare
659           // operation and on the entry block argument.
660           // In case of host associated use, the declare operation
661           // is the only carrier of the variable attributes,
662           // so we have to collect them here.
663           attributes |= getAttrsFromVariable(varIf);
664           isCapturedInInternalProcedure |=
665               varIf.isCapturedInInternalProcedure();
666           if (varIf.isHostAssoc()) {
667             // Do not track past such DeclareOp, because it does not
668             // currently provide any useful information. The host associated
669             // access will end up dereferencing the host association tuple,
670             // so we may as well stop right now.
671             v = defOp->getResult(0);
672             // TODO: if the host associated variable is a dummy argument
673             // of the host, I think, we can treat it as SourceKind::Argument
674             // for the purpose of alias analysis inside the internal procedure.
675             type = SourceKind::HostAssoc;
676             breakFromLoop = true;
677             return;
678           }
679           if (getLastInstantiationPoint) {
680             // Fetch only the innermost instantiation point.
681             if (!instantiationPoint)
682               instantiationPoint = op;
683 
684             if (op.getDummyScope()) {
685               // Do not track past DeclareOp that has the dummy_scope
686               // operand. This DeclareOp is known to represent
687               // a dummy argument for some runtime instantiation
688               // of a procedure.
689               type = SourceKind::Argument;
690               breakFromLoop = true;
691               return;
692             }
693           } else {
694             instantiationPoint = op;
695           }
696           if (isPrivateItem) {
697             type = SourceKind::Allocate;
698             breakFromLoop = true;
699             return;
700           }
701           // TODO: Look for the fortran attributes present on the operation
702           // Track further through the operand
703           v = op.getMemref();
704           defOp = v.getDefiningOp();
705         })
706         .Case<hlfir::DesignateOp>([&](auto op) {
707           auto varIf = llvm::cast<fir::FortranVariableOpInterface>(defOp);
708           attributes |= getAttrsFromVariable(varIf);
709           // Track further through the memory indexed into
710           // => if the source arrays/structures don't alias then nor do the
711           //    results of hlfir.designate
712           v = op.getMemref();
713           defOp = v.getDefiningOp();
714           // TODO: there will be some cases which provably don't alias if one
715           // takes into account the component or indices, which are currently
716           // ignored here - leading to false positives
717           // because of this limitation, we need to make sure we never return
718           // MustAlias after going through a designate operation
719           approximateSource = true;
720           if (mlir::isa<fir::BaseBoxType>(v.getType()))
721             followBoxData = true;
722         })
723         .Default([&](auto op) {
724           defOp = nullptr;
725           breakFromLoop = true;
726         });
727   }
728   if (!defOp && type == SourceKind::Unknown) {
729     // Check if the memory source is coming through a dummy argument.
730     if (isDummyArgument(v)) {
731       type = SourceKind::Argument;
732       ty = v.getType();
733       if (fir::valueHasFirAttribute(v, fir::getTargetAttrName()))
734         attributes.set(Attribute::Target);
735 
736       if (isPointerReference(ty))
737         attributes.set(Attribute::Pointer);
738     } else if (isEvaluateInMemoryBlockArg(v)) {
739       // hlfir.eval_in_mem block operands is allocated by the operation.
740       type = SourceKind::Allocate;
741       ty = v.getType();
742     }
743   }
744 
745   if (type == SourceKind::Global) {
746     return {{global, instantiationPoint, followingData},
747             type,
748             ty,
749             attributes,
750             approximateSource,
751             isCapturedInInternalProcedure};
752   }
753   return {{v, instantiationPoint, followingData},
754           type,
755           ty,
756           attributes,
757           approximateSource,
758           isCapturedInInternalProcedure};
759 }
760 
761 } // namespace fir
762