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 ®ion, 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