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

source code of flang/lib/Optimizer/Analysis/AliasAnalysis.cpp