1212#include " flang/Optimizer/Dialect/FIRType.h"
1313#include " flang/Optimizer/Dialect/FortranVariableInterface.h"
1414#include " flang/Optimizer/HLFIR/HLFIROps.h"
15+ #include " flang/Optimizer/Support/InternalNames.h"
1516#include " mlir/Analysis/AliasAnalysis.h"
1617#include " mlir/Dialect/OpenMP/OpenMPDialect.h"
1718#include " mlir/Dialect/OpenMP/OpenMPInterfaces.h"
@@ -96,6 +97,17 @@ bool AliasAnalysis::Source::isBoxData() const {
9697 origin.isData ;
9798}
9899
100+ bool AliasAnalysis::Source::isFortranUserVariable () const {
101+ if (!origin.instantiationPoint )
102+ return false ;
103+ return llvm::TypeSwitch<mlir::Operation *, bool >(origin.instantiationPoint )
104+ .template Case <fir::DeclareOp, hlfir::DeclareOp>([&](auto declOp) {
105+ return fir::NameUniquer::deconstruct (declOp.getUniqName ()).first ==
106+ fir::NameUniquer::NameKind::VARIABLE;
107+ })
108+ .Default ([&](auto op) { return false ; });
109+ }
110+
99111bool AliasAnalysis::Source::mayBeDummyArgOrHostAssoc () const {
100112 return kind != SourceKind::Allocate && kind != SourceKind::Global;
101113}
@@ -329,14 +341,92 @@ AliasResult AliasAnalysis::alias(Source lhsSrc, Source rhsSrc, mlir::Value lhs,
329341// AliasAnalysis: getModRef
330342// ===----------------------------------------------------------------------===//
331343
344+ static bool isSavedLocal (const fir::AliasAnalysis::Source &src) {
345+ if (auto symRef = llvm::dyn_cast<mlir::SymbolRefAttr>(src.origin .u )) {
346+ auto [nameKind, deconstruct] =
347+ fir::NameUniquer::deconstruct (symRef.getLeafReference ().getValue ());
348+ return nameKind == fir::NameUniquer::NameKind::VARIABLE &&
349+ !deconstruct.procs .empty ();
350+ }
351+ return false ;
352+ }
353+
354+ static bool isCallToFortranUserProcedure (fir::CallOp call) {
355+ // TODO: indirect calls are excluded by these checks. Maybe some attribute is
356+ // needed to flag user calls in this case.
357+ if (fir::hasBindcAttr (call))
358+ return true ;
359+ if (std::optional<mlir::SymbolRefAttr> callee = call.getCallee ())
360+ return fir::NameUniquer::deconstruct (callee->getLeafReference ().getValue ())
361+ .first == fir::NameUniquer::NameKind::PROCEDURE;
362+ return false ;
363+ }
364+
365+ static ModRefResult getCallModRef (fir::CallOp call, mlir::Value var) {
366+ // TODO: limit to Fortran functions??
367+ // 1. Detect variables that can be accessed indirectly.
368+ fir::AliasAnalysis aliasAnalysis;
369+ fir::AliasAnalysis::Source varSrc = aliasAnalysis.getSource (var);
370+ // If the variable is not a user variable, we cannot safely assume that
371+ // Fortran semantics apply (e.g., a bare alloca/allocmem result may very well
372+ // be placed in an allocatable/pointer descriptor and escape).
373+
374+ // All the logic below is based on Fortran semantics and only holds if this
375+ // is a call to a procedure from the Fortran source and this is a variable
376+ // from the Fortran source. Compiler generated temporaries or functions may
377+ // not adhere to this semantic.
378+ // TODO: add some opt-in or op-out mechanism for compiler generated temps.
379+ // An example of something currently problematic is the allocmem generated for
380+ // ALLOCATE of allocatable target. It currently does not have the target
381+ // attribute, which would lead this analysis to believe it cannot escape.
382+ if (!varSrc.isFortranUserVariable () || !isCallToFortranUserProcedure (call))
383+ return ModRefResult::getModAndRef ();
384+ // Pointer and target may have been captured.
385+ if (varSrc.isTargetOrPointer ())
386+ return ModRefResult::getModAndRef ();
387+ // Host associated variables may be addressed indirectly via an internal
388+ // function call, whether the call is in the parent or an internal procedure.
389+ // Note that the host associated/internal procedure may be referenced
390+ // indirectly inside calls to non internal procedure. This is because internal
391+ // procedures may be captured or passed. As this is tricky to analyze, always
392+ // consider such variables may be accessed in any calls.
393+ if (varSrc.kind == fir::AliasAnalysis::SourceKind::HostAssoc ||
394+ varSrc.isCapturedInInternalProcedure )
395+ return ModRefResult::getModAndRef ();
396+ // At that stage, it has been ruled out that local (including the saved ones)
397+ // and dummy cannot be indirectly accessed in the call.
398+ if (varSrc.kind != fir::AliasAnalysis::SourceKind::Allocate &&
399+ !varSrc.isDummyArgument ()) {
400+ if (varSrc.kind != fir::AliasAnalysis::SourceKind::Global ||
401+ !isSavedLocal (varSrc))
402+ return ModRefResult::getModAndRef ();
403+ }
404+ // 2. Check if the variable is passed via the arguments.
405+ for (auto arg : call.getArgs ()) {
406+ if (fir::conformsWithPassByRef (arg.getType ()) &&
407+ !aliasAnalysis.alias (arg, var).isNo ()) {
408+ // TODO: intent(in) would allow returning Ref here. This can be obtained
409+ // in the func.func attributes for direct calls, but the module lookup is
410+ // linear with the number of MLIR symbols, which would introduce a pseudo
411+ // quadratic behavior num_calls * num_func.
412+ return ModRefResult::getModAndRef ();
413+ }
414+ }
415+ // The call cannot access the variable.
416+ return ModRefResult::getNoModRef ();
417+ }
418+
332419// / This is mostly inspired by MLIR::LocalAliasAnalysis with 2 notable
333420// / differences 1) Regions are not handled here but will be handled by a data
334421// / flow analysis to come 2) Allocate and Free effects are considered
335422// / modifying
336423ModRefResult AliasAnalysis::getModRef (Operation *op, Value location) {
337424 MemoryEffectOpInterface interface = dyn_cast<MemoryEffectOpInterface>(op);
338- if (!interface)
425+ if (!interface) {
426+ if (auto call = llvm::dyn_cast<fir::CallOp>(op))
427+ return getCallModRef (call, location);
339428 return ModRefResult::getModAndRef ();
429+ }
340430
341431 // Build a ModRefResult by merging the behavior of the effects of this
342432 // operation.
@@ -408,19 +498,20 @@ static Value getPrivateArg(omp::BlockArgOpenMPOpInterface &argIface,
408498}
409499
410500AliasAnalysis::Source AliasAnalysis::getSource (mlir::Value v,
411- bool getInstantiationPoint ) {
501+ bool getLastInstantiationPoint ) {
412502 auto *defOp = v.getDefiningOp ();
413503 SourceKind type{SourceKind::Unknown};
414504 mlir::Type ty;
415505 bool breakFromLoop{false };
416506 bool approximateSource{false };
507+ bool isCapturedInInternalProcedure{false };
417508 bool followBoxData{mlir::isa<fir::BaseBoxType>(v.getType ())};
418509 bool isBoxRef{fir::isa_ref_type (v.getType ()) &&
419510 mlir::isa<fir::BaseBoxType>(fir::unwrapRefType (v.getType ()))};
420511 bool followingData = !isBoxRef;
421512 mlir::SymbolRefAttr global;
422513 Source::Attributes attributes;
423- mlir::Value instantiationPoint;
514+ mlir::Operation * instantiationPoint{ nullptr } ;
424515 while (defOp && !breakFromLoop) {
425516 ty = defOp->getResultTypes ()[0 ];
426517 llvm::TypeSwitch<Operation *>(defOp)
@@ -548,6 +639,8 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
548639 // is the only carrier of the variable attributes,
549640 // so we have to collect them here.
550641 attributes |= getAttrsFromVariable (varIf);
642+ isCapturedInInternalProcedure |=
643+ varIf.isCapturedInInternalProcedure ();
551644 if (varIf.isHostAssoc ()) {
552645 // Do not track past such DeclareOp, because it does not
553646 // currently provide any useful information. The host associated
@@ -561,10 +654,10 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
561654 breakFromLoop = true ;
562655 return ;
563656 }
564- if (getInstantiationPoint ) {
657+ if (getLastInstantiationPoint ) {
565658 // Fetch only the innermost instantiation point.
566659 if (!instantiationPoint)
567- instantiationPoint = op-> getResult ( 0 ) ;
660+ instantiationPoint = op;
568661
569662 if (op.getDummyScope ()) {
570663 // Do not track past DeclareOp that has the dummy_scope
@@ -575,6 +668,8 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
575668 breakFromLoop = true ;
576669 return ;
577670 }
671+ } else {
672+ instantiationPoint = op;
578673 }
579674 // TODO: Look for the fortran attributes present on the operation
580675 // Track further through the operand
@@ -620,13 +715,15 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
620715 type,
621716 ty,
622717 attributes,
623- approximateSource};
718+ approximateSource,
719+ isCapturedInInternalProcedure};
624720 }
625721 return {{v, instantiationPoint, followingData},
626722 type,
627723 ty,
628724 attributes,
629- approximateSource};
725+ approximateSource,
726+ isCapturedInInternalProcedure};
630727}
631728
632729} // namespace fir
0 commit comments