3152 lines · cpp
1//===-- ConvertCall.cpp ---------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/10//11//===----------------------------------------------------------------------===//12 13#include "flang/Lower/ConvertCall.h"14#include "flang/Lower/Allocatable.h"15#include "flang/Lower/ConvertExprToHLFIR.h"16#include "flang/Lower/ConvertProcedureDesignator.h"17#include "flang/Lower/ConvertVariable.h"18#include "flang/Lower/CustomIntrinsicCall.h"19#include "flang/Lower/HlfirIntrinsics.h"20#include "flang/Lower/PFTBuilder.h"21#include "flang/Lower/StatementContext.h"22#include "flang/Lower/SymbolMap.h"23#include "flang/Optimizer/Builder/BoxValue.h"24#include "flang/Optimizer/Builder/CUFCommon.h"25#include "flang/Optimizer/Builder/Character.h"26#include "flang/Optimizer/Builder/FIRBuilder.h"27#include "flang/Optimizer/Builder/HLFIRTools.h"28#include "flang/Optimizer/Builder/IntrinsicCall.h"29#include "flang/Optimizer/Builder/LowLevelIntrinsics.h"30#include "flang/Optimizer/Builder/MutableBox.h"31#include "flang/Optimizer/Builder/Runtime/CUDA/Descriptor.h"32#include "flang/Optimizer/Builder/Runtime/Derived.h"33#include "flang/Optimizer/Builder/Todo.h"34#include "flang/Optimizer/Dialect/CUF/CUFOps.h"35#include "flang/Optimizer/Dialect/FIROpsSupport.h"36#include "flang/Optimizer/HLFIR/HLFIROps.h"37#include "mlir/IR/IRMapping.h"38#include "llvm/ADT/TypeSwitch.h"39#include "llvm/Support/CommandLine.h"40#include "llvm/Support/Debug.h"41#include <optional>42 43#define DEBUG_TYPE "flang-lower-expr"44 45static llvm::cl::opt<bool> useHlfirIntrinsicOps(46 "use-hlfir-intrinsic-ops", llvm::cl::init(true),47 llvm::cl::desc("Lower via HLFIR transformational intrinsic operations such "48 "as hlfir.sum"));49 50static constexpr char tempResultName[] = ".tmp.func_result";51 52/// Helper to package a Value and its properties into an ExtendedValue.53static fir::ExtendedValue toExtendedValue(mlir::Location loc, mlir::Value base,54 llvm::ArrayRef<mlir::Value> extents,55 llvm::ArrayRef<mlir::Value> lengths) {56 mlir::Type type = base.getType();57 if (mlir::isa<fir::BaseBoxType>(type))58 return fir::BoxValue(base, /*lbounds=*/{}, lengths, extents);59 type = fir::unwrapRefType(type);60 if (mlir::isa<fir::BaseBoxType>(type))61 return fir::MutableBoxValue(base, lengths, /*mutableProperties*/ {});62 if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(type)) {63 if (seqTy.getDimension() != extents.size())64 fir::emitFatalError(loc, "incorrect number of extents for array");65 if (mlir::isa<fir::CharacterType>(seqTy.getEleTy())) {66 if (lengths.empty())67 fir::emitFatalError(loc, "missing length for character");68 assert(lengths.size() == 1);69 return fir::CharArrayBoxValue(base, lengths[0], extents);70 }71 return fir::ArrayBoxValue(base, extents);72 }73 if (mlir::isa<fir::CharacterType>(type)) {74 if (lengths.empty())75 fir::emitFatalError(loc, "missing length for character");76 assert(lengths.size() == 1);77 return fir::CharBoxValue(base, lengths[0]);78 }79 return base;80}81 82/// Lower a type(C_PTR/C_FUNPTR) argument with VALUE attribute into a83/// reference. A C pointer can correspond to a Fortran dummy argument of type84/// C_PTR with the VALUE attribute. (see 18.3.6 note 3).85static mlir::Value genRecordCPtrValueArg(fir::FirOpBuilder &builder,86 mlir::Location loc, mlir::Value rec,87 mlir::Type ty) {88 mlir::Value cAddr = fir::factory::genCPtrOrCFunptrAddr(builder, loc, rec, ty);89 mlir::Value cVal = fir::LoadOp::create(builder, loc, cAddr);90 return builder.createConvert(loc, cAddr.getType(), cVal);91}92 93// Find the argument that corresponds to the host associations.94// Verify some assumptions about how the signature was built here.95[[maybe_unused]] static unsigned findHostAssocTuplePos(mlir::func::FuncOp fn) {96 // Scan the argument list from last to first as the host associations are97 // appended for now.98 for (unsigned i = fn.getNumArguments(); i > 0; --i)99 if (fn.getArgAttr(i - 1, fir::getHostAssocAttrName())) {100 // Host assoc tuple must be last argument (for now).101 assert(i == fn.getNumArguments() && "tuple must be last");102 return i - 1;103 }104 llvm_unreachable("anyFuncArgsHaveAttr failed");105}106 107mlir::Value108Fortran::lower::argumentHostAssocs(Fortran::lower::AbstractConverter &converter,109 mlir::Value arg) {110 if (auto addr = mlir::dyn_cast_or_null<fir::AddrOfOp>(arg.getDefiningOp())) {111 auto &builder = converter.getFirOpBuilder();112 if (auto funcOp = builder.getNamedFunction(addr.getSymbol()))113 if (fir::anyFuncArgsHaveAttr(funcOp, fir::getHostAssocAttrName()))114 return converter.hostAssocTupleValue();115 }116 return {};117}118 119static bool mustCastFuncOpToCopeWithImplicitInterfaceMismatch(120 mlir::Location loc, Fortran::lower::AbstractConverter &converter,121 mlir::FunctionType callSiteType, mlir::FunctionType funcOpType) {122 // Deal with argument number mismatch by making a function pointer so123 // that function type cast can be inserted. Do not emit a warning here124 // because this can happen in legal program if the function is not125 // defined here and it was first passed as an argument without any more126 // information.127 if (callSiteType.getNumResults() != funcOpType.getNumResults() ||128 callSiteType.getNumInputs() != funcOpType.getNumInputs())129 return true;130 131 // Implicit interface result type mismatch are not standard Fortran, but132 // some compilers are not complaining about it. The front end is not133 // protecting lowering from this currently. Support this with a134 // discouraging warning.135 // Cast the actual function to the current caller implicit type because136 // that is the behavior we would get if we could not see the definition.137 if (callSiteType.getResults() != funcOpType.getResults()) {138 LLVM_DEBUG(mlir::emitWarning(139 loc, "a return type mismatch is not standard compliant and may "140 "lead to undefined behavior."));141 return true;142 }143 144 // In HLFIR, there is little attempt to cope with implicit interface145 // mismatch on the arguments. The argument are always prepared according146 // to the implicit interface. Cast the actual function if any of the147 // argument mismatch cannot be dealt with a simple fir.convert.148 if (converter.getLoweringOptions().getLowerToHighLevelFIR())149 for (auto [actualType, dummyType] :150 llvm::zip(callSiteType.getInputs(), funcOpType.getInputs()))151 if (actualType != dummyType &&152 !fir::ConvertOp::canBeConverted(actualType, dummyType))153 return true;154 return false;155}156 157static mlir::Value readDim3Value(fir::FirOpBuilder &builder, mlir::Location loc,158 mlir::Value dim3Addr, llvm::StringRef comp) {159 mlir::Type i32Ty = builder.getI32Type();160 mlir::Type refI32Ty = fir::ReferenceType::get(i32Ty);161 llvm::SmallVector<mlir::Value> lenParams;162 163 mlir::Value designate = hlfir::DesignateOp::create(164 builder, loc, refI32Ty, dim3Addr, /*component=*/comp,165 /*componentShape=*/mlir::Value{}, hlfir::DesignateOp::Subscripts{},166 /*substring=*/mlir::ValueRange{}, /*complexPartAttr=*/std::nullopt,167 mlir::Value{}, lenParams);168 169 return hlfir::loadTrivialScalar(loc, builder, hlfir::Entity{designate});170}171 172static mlir::Value remapActualToDummyDescriptor(173 mlir::Location loc, Fortran::lower::AbstractConverter &converter,174 Fortran::lower::SymMap &symMap,175 const Fortran::lower::CallerInterface::PassedEntity &arg,176 Fortran::lower::CallerInterface &caller, bool isBindcCall) {177 fir::FirOpBuilder &builder = converter.getFirOpBuilder();178 mlir::IndexType idxTy = builder.getIndexType();179 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);180 Fortran::lower::StatementContext localStmtCtx;181 auto lowerSpecExpr = [&](const auto &expr,182 bool isAssumedSizeExtent) -> mlir::Value {183 mlir::Value convertExpr = builder.createConvert(184 loc, idxTy, fir::getBase(converter.genExprValue(expr, localStmtCtx)));185 if (isAssumedSizeExtent)186 return convertExpr;187 return fir::factory::genMaxWithZero(builder, loc, convertExpr);188 };189 bool mapSymbols = caller.mustMapInterfaceSymbolsForDummyArgument(arg);190 if (mapSymbols) {191 symMap.pushScope();192 const Fortran::semantics::Symbol *sym = caller.getDummySymbol(arg);193 assert(sym && "call must have explicit interface to map interface symbols");194 Fortran::lower::mapCallInterfaceSymbolsForDummyArgument(converter, caller,195 symMap, *sym);196 }197 llvm::SmallVector<mlir::Value> extents;198 llvm::SmallVector<mlir::Value> lengths;199 mlir::Type dummyBoxType = caller.getDummyArgumentType(arg);200 mlir::Type dummyBaseType = fir::unwrapPassByRefType(dummyBoxType);201 if (mlir::isa<fir::SequenceType>(dummyBaseType))202 caller.walkDummyArgumentExtents(203 arg, [&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) {204 extents.emplace_back(lowerSpecExpr(e, isAssumedSizeExtent));205 });206 mlir::Value shape;207 if (!extents.empty()) {208 if (isBindcCall) {209 // Preserve zero lower bounds (see F'2023 18.5.3).210 llvm::SmallVector<mlir::Value> lowerBounds(extents.size(), zero);211 shape = builder.genShape(loc, lowerBounds, extents);212 } else {213 shape = builder.genShape(loc, extents);214 }215 }216 217 hlfir::Entity explicitArgument = hlfir::Entity{caller.getInput(arg)};218 mlir::Type dummyElementType = fir::unwrapSequenceType(dummyBaseType);219 if (auto recType = llvm::dyn_cast<fir::RecordType>(dummyElementType))220 if (recType.getNumLenParams() > 0)221 TODO(loc, "sequence association of length parameterized derived type "222 "dummy arguments");223 if (fir::isa_char(dummyElementType))224 lengths.emplace_back(hlfir::genCharLength(loc, builder, explicitArgument));225 mlir::Value baseAddr =226 hlfir::genVariableRawAddress(loc, builder, explicitArgument);227 baseAddr = builder.createConvert(loc, fir::ReferenceType::get(dummyBaseType),228 baseAddr);229 mlir::Value mold;230 if (fir::isPolymorphicType(dummyBoxType))231 mold = explicitArgument;232 mlir::Value remapped =233 fir::EmboxOp::create(builder, loc, dummyBoxType, baseAddr, shape,234 /*slice=*/mlir::Value{}, lengths, mold);235 if (mapSymbols)236 symMap.popScope();237 return remapped;238}239 240/// Create a descriptor for sequenced associated descriptor that are passed241/// by descriptor. Sequence association (F'2023 15.5.2.12) implies that the242/// dummy shape and rank need to not be the same as the actual argument. This243/// helper creates a descriptor based on the dummy shape and rank (sequence244/// association can only happen with explicit and assumed-size array) so that it245/// is safe to assume the rank of the incoming descriptor inside the callee.246/// This helper must be called once all the actual arguments have been lowered247/// and placed inside "caller". Copy-in/copy-out must already have been248/// generated if needed using the actual argument shape (the dummy shape may be249/// assumed-size).250static void remapActualToDummyDescriptors(251 mlir::Location loc, Fortran::lower::AbstractConverter &converter,252 Fortran::lower::SymMap &symMap,253 const Fortran::lower::PreparedActualArguments &loweredActuals,254 Fortran::lower::CallerInterface &caller, bool isBindcCall) {255 fir::FirOpBuilder &builder = converter.getFirOpBuilder();256 for (auto [preparedActual, arg] :257 llvm::zip(loweredActuals, caller.getPassedArguments())) {258 if (arg.isSequenceAssociatedDescriptor()) {259 if (!preparedActual.value().handleDynamicOptional()) {260 mlir::Value remapped = remapActualToDummyDescriptor(261 loc, converter, symMap, arg, caller, isBindcCall);262 caller.placeInput(arg, remapped);263 } else {264 // Absent optional actual argument descriptor cannot be read and265 // remapped unconditionally.266 mlir::Type dummyType = caller.getDummyArgumentType(arg);267 mlir::Value isPresent = preparedActual.value().getIsPresent();268 auto &argLambdaCapture = arg;269 mlir::Value remapped =270 builder271 .genIfOp(loc, {dummyType}, isPresent,272 /*withElseRegion=*/true)273 .genThen([&]() {274 mlir::Value newBox = remapActualToDummyDescriptor(275 loc, converter, symMap, argLambdaCapture, caller,276 isBindcCall);277 fir::ResultOp::create(builder, loc, newBox);278 })279 .genElse([&]() {280 mlir::Value absent =281 fir::AbsentOp::create(builder, loc, dummyType);282 fir::ResultOp::create(builder, loc, absent);283 })284 .getResults()[0];285 caller.placeInput(arg, remapped);286 }287 }288 }289}290 291static void292getResultLengthFromElementalOp(fir::FirOpBuilder &builder,293 llvm::SmallVectorImpl<mlir::Value> &lengths) {294 auto elemental = llvm::dyn_cast_or_null<hlfir::ElementalOp>(295 builder.getInsertionBlock()->getParentOp());296 if (elemental)297 for (mlir::Value len : elemental.getTypeparams())298 lengths.push_back(len);299}300 301std::pair<Fortran::lower::LoweredResult, bool>302Fortran::lower::genCallOpAndResult(303 mlir::Location loc, Fortran::lower::AbstractConverter &converter,304 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,305 Fortran::lower::CallerInterface &caller, mlir::FunctionType callSiteType,306 std::optional<mlir::Type> resultType, bool isElemental) {307 fir::FirOpBuilder &builder = converter.getFirOpBuilder();308 using PassBy = Fortran::lower::CallerInterface::PassEntityBy;309 bool mustPopSymMap = false;310 311 llvm::SmallVector<mlir::Value> resultLengths;312 if (isElemental)313 getResultLengthFromElementalOp(builder, resultLengths);314 if (caller.mustMapInterfaceSymbolsForResult() && resultLengths.empty()) {315 // Do not map the dummy symbols again inside the loop to compute elemental316 // function result whose length was already computed outside of the loop.317 symMap.pushScope();318 mustPopSymMap = true;319 Fortran::lower::mapCallInterfaceSymbolsForResult(converter, caller, symMap);320 }321 // If this is an indirect call, retrieve the function address. Also retrieve322 // the result length if this is a character function (note that this length323 // will be used only if there is no explicit length in the local interface).324 mlir::Value funcPointer;325 mlir::Value charFuncPointerLength;326 if (const Fortran::evaluate::ProcedureDesignator *procDesignator =327 caller.getIfIndirectCall()) {328 if (mlir::Value passedArg = caller.getIfPassedArg()) {329 // Procedure pointer component call with PASS argument. To avoid330 // "double" lowering of the ComponentRef, semantics only place the331 // ComponentRef in the ActualArguments, not in the ProcedureDesignator (332 // that is only the component symbol).333 // Fetch the passed argument and addresses of its procedure pointer334 // component.335 funcPointer = Fortran::lower::derefPassProcPointerComponent(336 loc, converter, *procDesignator, passedArg, symMap, stmtCtx);337 } else {338 Fortran::lower::SomeExpr expr{*procDesignator};339 fir::ExtendedValue loweredProc =340 converter.genExprAddr(loc, expr, stmtCtx);341 funcPointer = fir::getBase(loweredProc);342 // Dummy procedure may have assumed length, in which case the result343 // length was passed along the dummy procedure.344 // This is not possible with procedure pointer components.345 if (const fir::CharBoxValue *charBox = loweredProc.getCharBox())346 charFuncPointerLength = charBox->getLen();347 }348 }349 const bool isExprCall =350 converter.getLoweringOptions().getLowerToHighLevelFIR() &&351 callSiteType.getNumResults() == 1 &&352 llvm::isa<fir::SequenceType>(callSiteType.getResult(0));353 354 mlir::IndexType idxTy = builder.getIndexType();355 auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value {356 mlir::Value convertExpr = builder.createConvert(357 loc, idxTy, fir::getBase(converter.genExprValue(expr, stmtCtx)));358 return fir::factory::genMaxWithZero(builder, loc, convertExpr);359 };360 mlir::Value arrayResultShape;361 hlfir::EvaluateInMemoryOp evaluateInMemory;362 auto allocatedResult = [&]() -> std::optional<fir::ExtendedValue> {363 llvm::SmallVector<mlir::Value> extents;364 llvm::SmallVector<mlir::Value> lengths;365 if (!caller.callerAllocateResult())366 return {};367 mlir::Type type = caller.getResultStorageType();368 if (mlir::isa<fir::SequenceType>(type))369 caller.walkResultExtents(370 [&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) {371 assert(!isAssumedSizeExtent && "result cannot be assumed-size");372 extents.emplace_back(lowerSpecExpr(e));373 });374 if (resultLengths.empty()) {375 caller.walkResultLengths(376 [&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) {377 assert(!isAssumedSizeExtent && "result cannot be assumed-size");378 lengths.emplace_back(lowerSpecExpr(e));379 });380 } else {381 // Use lengths precomputed before elemental loops.382 lengths = resultLengths;383 }384 385 // Result length parameters should not be provided to box storage386 // allocation and save_results, but they are still useful information to387 // keep in the ExtendedValue if non-deferred.388 if (!mlir::isa<fir::BoxType>(type)) {389 if (fir::isa_char(fir::unwrapSequenceType(type)) && lengths.empty()) {390 // Calling an assumed length function. This is only possible if this391 // is a call to a character dummy procedure.392 if (!charFuncPointerLength)393 fir::emitFatalError(loc, "failed to retrieve character function "394 "length while calling it");395 lengths.push_back(charFuncPointerLength);396 }397 resultLengths = lengths;398 }399 400 if (!extents.empty())401 arrayResultShape = builder.genShape(loc, extents);402 403 if (isExprCall) {404 mlir::Type exprType = hlfir::getExprType(type);405 evaluateInMemory = hlfir::EvaluateInMemoryOp::create(406 builder, loc, exprType, arrayResultShape, resultLengths);407 builder.setInsertionPointToStart(&evaluateInMemory.getBody().front());408 return toExtendedValue(loc, evaluateInMemory.getMemory(), extents,409 lengths);410 }411 412 if ((!extents.empty() || !lengths.empty()) && !isElemental) {413 // Note: in the elemental context, the alloca ownership inside the414 // elemental region is implicit, and later pass in lowering (stack415 // reclaim) fir.do_loop will be in charge of emitting any stack416 // save/restore if needed.417 auto *bldr = &converter.getFirOpBuilder();418 mlir::Value sp = bldr->genStackSave(loc);419 stmtCtx.attachCleanup(420 [bldr, loc, sp]() { bldr->genStackRestore(loc, sp); });421 }422 mlir::Value temp =423 builder.createTemporary(loc, type, ".result", extents, resultLengths);424 return toExtendedValue(loc, temp, extents, lengths);425 }();426 427 if (mustPopSymMap)428 symMap.popScope();429 430 // Place allocated result431 if (allocatedResult) {432 if (std::optional<Fortran::lower::CallInterface<433 Fortran::lower::CallerInterface>::PassedEntity>434 resultArg = caller.getPassedResult()) {435 if (resultArg->passBy == PassBy::AddressAndLength)436 caller.placeAddressAndLengthInput(*resultArg,437 fir::getBase(*allocatedResult),438 fir::getLen(*allocatedResult));439 else if (resultArg->passBy == PassBy::BaseAddress)440 caller.placeInput(*resultArg, fir::getBase(*allocatedResult));441 else442 fir::emitFatalError(443 loc, "only expect character scalar result to be passed by ref");444 }445 }446 447 // In older Fortran, procedure argument types are inferred. This may lead448 // different view of what the function signature is in different locations.449 // Casts are inserted as needed below to accommodate this.450 451 // The mlir::func::FuncOp type prevails, unless it has a different number of452 // arguments which can happen in legal program if it was passed as a dummy453 // procedure argument earlier with no further type information.454 mlir::SymbolRefAttr funcSymbolAttr;455 bool addHostAssociations = false;456 if (!funcPointer) {457 mlir::FunctionType funcOpType = caller.getFuncOp().getFunctionType();458 mlir::SymbolRefAttr symbolAttr =459 builder.getSymbolRefAttr(caller.getMangledName());460 if (callSiteType.getNumResults() == funcOpType.getNumResults() &&461 callSiteType.getNumInputs() + 1 == funcOpType.getNumInputs() &&462 fir::anyFuncArgsHaveAttr(caller.getFuncOp(),463 fir::getHostAssocAttrName())) {464 // The number of arguments is off by one, and we're lowering a function465 // with host associations. Modify call to include host associations466 // argument by appending the value at the end of the operands.467 assert(funcOpType.getInput(findHostAssocTuplePos(caller.getFuncOp())) ==468 converter.hostAssocTupleValue().getType());469 addHostAssociations = true;470 }471 // When this is not a call to an internal procedure (where there is a472 // mismatch due to the extra argument, but the interface is otherwise473 // explicit and safe), handle interface mismatch due to F77 implicit474 // interface "abuse" with a function address cast if needed.475 if (!addHostAssociations &&476 mustCastFuncOpToCopeWithImplicitInterfaceMismatch(477 loc, converter, callSiteType, funcOpType))478 funcPointer = fir::AddrOfOp::create(builder, loc, funcOpType, symbolAttr);479 else480 funcSymbolAttr = symbolAttr;481 482 // Issue a warning if the procedure name conflicts with483 // a runtime function name a call to which has been already484 // lowered (implying that the FuncOp has been created).485 // The behavior is undefined in this case.486 if (caller.getFuncOp()->hasAttrOfType<mlir::UnitAttr>(487 fir::FIROpsDialect::getFirRuntimeAttrName()))488 LLVM_DEBUG(mlir::emitWarning(489 loc,490 llvm::Twine("function name '") +491 llvm::Twine(symbolAttr.getLeafReference()) +492 llvm::Twine("' conflicts with a runtime function name used by "493 "Flang - this may lead to undefined behavior")));494 }495 496 mlir::FunctionType funcType =497 funcPointer ? callSiteType : caller.getFuncOp().getFunctionType();498 llvm::SmallVector<mlir::Value> operands;499 // First operand of indirect call is the function pointer. Cast it to500 // required function type for the call to handle procedures that have a501 // compatible interface in Fortran, but that have different signatures in502 // FIR.503 if (funcPointer) {504 operands.push_back(505 mlir::isa<fir::BoxProcType>(funcPointer.getType())506 ? fir::BoxAddrOp::create(builder, loc, funcType, funcPointer)507 : builder.createConvert(loc, funcType, funcPointer));508 }509 510 // Deal with potential mismatches in arguments types. Passing an array to a511 // scalar argument should for instance be tolerated here.512 for (auto [fst, snd] : llvm::zip(caller.getInputs(), funcType.getInputs())) {513 // When passing arguments to a procedure that can be called by implicit514 // interface, allow any character actual arguments to be passed to dummy515 // arguments of any type and vice versa.516 mlir::Value cast;517 auto *context = builder.getContext();518 519 if (mlir::isa<fir::BoxProcType>(snd) &&520 mlir::isa<mlir::FunctionType>(fst.getType())) {521 mlir::FunctionType funcTy = mlir::FunctionType::get(context, {}, {});522 fir::BoxProcType boxProcTy = builder.getBoxProcType(funcTy);523 if (mlir::Value host = argumentHostAssocs(converter, fst)) {524 cast = fir::EmboxProcOp::create(builder, loc, boxProcTy,525 llvm::ArrayRef<mlir::Value>{fst, host});526 } else {527 cast = fir::EmboxProcOp::create(builder, loc, boxProcTy, fst);528 }529 } else {530 mlir::Type fromTy = fir::unwrapRefType(fst.getType());531 if (fir::isa_builtin_cptr_type(fromTy) &&532 Fortran::lower::isCPtrArgByValueType(snd)) {533 cast = genRecordCPtrValueArg(builder, loc, fst, fromTy);534 } else if (fir::isa_derived(snd) && !fir::isa_derived(fst.getType())) {535 // TODO: remove this TODO once the old lowering is gone.536 TODO(loc, "derived type argument passed by value");537 } else {538 // With the lowering to HLFIR, box arguments have already been built539 // according to the attributes, rank, bounds, and type they should have.540 // Do not attempt any reboxing here that could break this.541 bool legacyLowering =542 !converter.getLoweringOptions().getLowerToHighLevelFIR();543 // When dealing with a dummy character argument (fir.boxchar), the544 // effective argument might be a non-character raw pointer. This may545 // happen when calling an implicit interface that was previously called546 // with a character argument, or when calling an explicit interface with547 // an IgnoreTKR dummy character arguments. Allow creating a fir.boxchar548 // from the raw pointer, which requires a non-trivial type conversion.549 const bool allowCharacterConversions = true;550 bool isVolatile = fir::isa_volatile_type(snd);551 cast = builder.createVolatileCast(loc, isVolatile, fst);552 cast = builder.convertWithSemantics(loc, snd, cast,553 allowCharacterConversions,554 /*allowRebox=*/legacyLowering);555 }556 }557 operands.push_back(cast);558 }559 560 // Add host associations as necessary.561 if (addHostAssociations)562 operands.push_back(converter.hostAssocTupleValue());563 564 mlir::Value callResult;565 unsigned callNumResults;566 fir::FortranProcedureFlagsEnumAttr procAttrs =567 caller.getProcedureAttrs(builder.getContext());568 569 if (converter.getLoweringOptions().getCUDARuntimeCheck()) {570 if (caller.getCallDescription().chevrons().empty() &&571 !cuf::isCUDADeviceContext(builder.getRegion())) {572 for (auto [oper, arg] :573 llvm::zip(operands, caller.getPassedArguments())) {574 if (arg.testTKR(Fortran::common::IgnoreTKR::Contiguous))575 continue;576 if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(oper.getType())) {577 const Fortran::semantics::Symbol *sym = caller.getDummySymbol(arg);578 if (sym && Fortran::evaluate::IsCUDADeviceSymbol(*sym))579 fir::runtime::cuda::genDescriptorCheckSection(builder, loc, oper);580 }581 }582 }583 }584 585 if (!caller.getCallDescription().chevrons().empty()) {586 // A call to a CUDA kernel with the chevron syntax.587 588 mlir::Type i32Ty = builder.getI32Type();589 mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1);590 591 mlir::Value grid_x, grid_y, grid_z;592 if (caller.getCallDescription().chevrons()[0].GetType()->category() ==593 Fortran::common::TypeCategory::Integer) {594 // If grid is an integer, it is converted to dim3(grid,1,1). Since z is595 // not used for the number of thread blocks, it is omitted in the op.596 grid_x = builder.createConvert(597 loc, i32Ty,598 fir::getBase(converter.genExprValue(599 caller.getCallDescription().chevrons()[0], stmtCtx)));600 grid_y = one;601 grid_z = one;602 } else {603 auto dim3Addr = converter.genExprAddr(604 caller.getCallDescription().chevrons()[0], stmtCtx);605 grid_x = readDim3Value(builder, loc, fir::getBase(dim3Addr), "x");606 grid_y = readDim3Value(builder, loc, fir::getBase(dim3Addr), "y");607 grid_z = readDim3Value(builder, loc, fir::getBase(dim3Addr), "z");608 }609 610 mlir::Value block_x, block_y, block_z;611 if (caller.getCallDescription().chevrons()[1].GetType()->category() ==612 Fortran::common::TypeCategory::Integer) {613 // If block is an integer, it is converted to dim3(block,1,1).614 block_x = builder.createConvert(615 loc, i32Ty,616 fir::getBase(converter.genExprValue(617 caller.getCallDescription().chevrons()[1], stmtCtx)));618 block_y = one;619 block_z = one;620 } else {621 auto dim3Addr = converter.genExprAddr(622 caller.getCallDescription().chevrons()[1], stmtCtx);623 block_x = readDim3Value(builder, loc, fir::getBase(dim3Addr), "x");624 block_y = readDim3Value(builder, loc, fir::getBase(dim3Addr), "y");625 block_z = readDim3Value(builder, loc, fir::getBase(dim3Addr), "z");626 }627 628 mlir::Value bytes; // bytes is optional.629 if (caller.getCallDescription().chevrons().size() > 2)630 bytes = builder.createConvert(631 loc, i32Ty,632 fir::getBase(converter.genExprValue(633 caller.getCallDescription().chevrons()[2], stmtCtx)));634 635 mlir::Value stream; // stream is optional.636 if (caller.getCallDescription().chevrons().size() > 3) {637 stream = fir::getBase(converter.genExprAddr(638 caller.getCallDescription().chevrons()[3], stmtCtx));639 if (!fir::unwrapRefType(stream.getType()).isInteger(64)) {640 auto i64Ty = mlir::IntegerType::get(builder.getContext(), 64);641 mlir::Value newStream = builder.createTemporary(loc, i64Ty);642 mlir::Value load = fir::LoadOp::create(builder, loc, stream);643 mlir::Value conv = fir::ConvertOp::create(builder, loc, i64Ty, load);644 fir::StoreOp::create(builder, loc, conv, newStream);645 stream = newStream;646 }647 }648 649 cuf::KernelLaunchOp::create(builder, loc, funcType.getResults(),650 funcSymbolAttr, grid_x, grid_y, grid_z, block_x,651 block_y, block_z, bytes, stream, operands,652 /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr);653 callNumResults = 0;654 } else if (caller.requireDispatchCall()) {655 // Procedure call requiring a dynamic dispatch. Call is created with656 // fir.dispatch.657 658 // Get the raw procedure name. The procedure name is not mangled in the659 // binding table, but there can be a suffix to distinguish bindings of660 // the same name (which happens only when PRIVATE bindings exist in661 // ancestor types in other modules).662 const auto &ultimateSymbol =663 caller.getCallDescription().proc().GetSymbol()->GetUltimate();664 std::string procName = ultimateSymbol.name().ToString();665 if (const auto &binding{666 ultimateSymbol.get<Fortran::semantics::ProcBindingDetails>()};667 binding.numPrivatesNotOverridden() > 0)668 procName += "."s + std::to_string(binding.numPrivatesNotOverridden());669 fir::DispatchOp dispatch;670 if (std::optional<unsigned> passArg = caller.getPassArgIndex()) {671 // PASS, PASS(arg-name)672 // Note that caller.getInputs is used instead of operands to get the673 // passed object because interface mismatch issues may have inserted a674 // cast to the operand with a different declared type, which would break675 // later type bound call resolution in the FIR to FIR pass.676 dispatch = fir::DispatchOp::create(677 builder, loc, funcType.getResults(), builder.getStringAttr(procName),678 caller.getInputs()[*passArg], operands,679 builder.getI32IntegerAttr(*passArg), /*arg_attrs=*/nullptr,680 /*res_attrs=*/nullptr, procAttrs);681 } else {682 // NOPASS683 const Fortran::evaluate::Component *component =684 caller.getCallDescription().proc().GetComponent();685 assert(component && "expect component for type-bound procedure call.");686 687 fir::ExtendedValue dataRefValue = Fortran::lower::convertDataRefToValue(688 loc, converter, component->base(), symMap, stmtCtx);689 mlir::Value passObject = fir::getBase(dataRefValue);690 691 if (fir::isa_ref_type(passObject.getType()))692 passObject = fir::LoadOp::create(builder, loc, passObject);693 dispatch = fir::DispatchOp::create(694 builder, loc, funcType.getResults(), builder.getStringAttr(procName),695 passObject, operands, nullptr, /*arg_attrs=*/nullptr,696 /*res_attrs=*/nullptr, procAttrs);697 }698 callNumResults = dispatch.getNumResults();699 if (callNumResults != 0)700 callResult = dispatch.getResult(0);701 } else {702 // Standard procedure call with fir.call.703 fir::FortranInlineEnumAttr inlineAttr;704 705 if (caller.getCallDescription().hasNoInline())706 inlineAttr = fir::FortranInlineEnumAttr::get(707 builder.getContext(), fir::FortranInlineEnum::no_inline);708 else if (caller.getCallDescription().hasInlineHint())709 inlineAttr = fir::FortranInlineEnumAttr::get(710 builder.getContext(), fir::FortranInlineEnum::inline_hint);711 else if (caller.getCallDescription().hasAlwaysInline())712 inlineAttr = fir::FortranInlineEnumAttr::get(713 builder.getContext(), fir::FortranInlineEnum::always_inline);714 auto call = fir::CallOp::create(715 builder, loc, funcType.getResults(), funcSymbolAttr, operands,716 /*arg_attrs=*/nullptr, /*res_attrs=*/nullptr, procAttrs, inlineAttr,717 /*accessGroups=*/mlir::ArrayAttr{});718 719 callNumResults = call.getNumResults();720 if (callNumResults != 0)721 callResult = call.getResult(0);722 }723 724 std::optional<Fortran::evaluate::DynamicType> retTy =725 caller.getCallDescription().proc().GetType();726 // With HLFIR lowering, isElemental must be set to true727 // if we are producing an elemental call. In this case,728 // the elemental results must not be destroyed, instead,729 // the resulting array result will be finalized/destroyed730 // as needed by hlfir.destroy.731 const bool mustFinalizeResult =732 !isElemental && callSiteType.getNumResults() > 0 &&733 !fir::isPointerType(callSiteType.getResult(0)) && retTy.has_value() &&734 (retTy->category() == Fortran::common::TypeCategory::Derived ||735 retTy->IsPolymorphic() || retTy->IsUnlimitedPolymorphic());736 737 if (caller.mustSaveResult()) {738 assert(allocatedResult.has_value());739 fir::SaveResultOp::create(builder, loc, callResult,740 fir::getBase(*allocatedResult), arrayResultShape,741 resultLengths);742 }743 744 if (evaluateInMemory) {745 builder.setInsertionPointAfter(evaluateInMemory);746 mlir::Value expr = evaluateInMemory.getResult();747 fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();748 if (!isElemental)749 stmtCtx.attachCleanup([bldr, loc, expr, mustFinalizeResult]() {750 hlfir::DestroyOp::create(*bldr, loc, expr,751 /*finalize=*/mustFinalizeResult);752 });753 return {LoweredResult{hlfir::EntityWithAttributes{expr}},754 mustFinalizeResult};755 }756 757 if (allocatedResult) {758 // The result must be optionally destroyed (if it is of a derived type759 // that may need finalization or deallocation of the components).760 // For an allocatable result we have to free the memory allocated761 // for the top-level entity. Note that the Destroy calls below762 // do not deallocate the top-level entity. The two clean-ups763 // must be pushed in reverse order, so that the final order is:764 // Destroy(desc)765 // free(desc->base_addr)766 allocatedResult->match(767 [&](const fir::MutableBoxValue &box) {768 if (box.isAllocatable()) {769 // 9.7.3.2 point 4. Deallocate allocatable results. Note that770 // finalization was done independently by calling771 // genDerivedTypeDestroy above and is not triggered by this inline772 // deallocation.773 fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();774 stmtCtx.attachCleanup([bldr, loc, box]() {775 fir::factory::genFreememIfAllocated(*bldr, loc, box);776 });777 }778 },779 [](const auto &) {});780 781 // 7.5.6.3 point 5. Derived-type finalization for nonpointer function.782 bool resultIsFinalized = false;783 // Check if the derived-type is finalizable if it is a monomorphic784 // derived-type.785 // For polymorphic and unlimited polymorphic enities call the runtime786 // in any cases.787 if (mustFinalizeResult) {788 if (retTy->IsPolymorphic() || retTy->IsUnlimitedPolymorphic()) {789 auto *bldr = &converter.getFirOpBuilder();790 stmtCtx.attachCleanup([bldr, loc, allocatedResult]() {791 fir::runtime::genDerivedTypeDestroy(*bldr, loc,792 fir::getBase(*allocatedResult));793 });794 resultIsFinalized = true;795 } else {796 const Fortran::semantics::DerivedTypeSpec &typeSpec =797 retTy->GetDerivedTypeSpec();798 // If the result type may require finalization799 // or have allocatable components, we need to make sure800 // everything is properly finalized/deallocated.801 if (Fortran::semantics::MayRequireFinalization(typeSpec) ||802 // We can use DerivedTypeDestroy even if finalization is not needed.803 hlfir::mayHaveAllocatableComponent(funcType.getResults()[0])) {804 auto *bldr = &converter.getFirOpBuilder();805 stmtCtx.attachCleanup([bldr, loc, allocatedResult]() {806 mlir::Value box = bldr->createBox(loc, *allocatedResult);807 fir::runtime::genDerivedTypeDestroy(*bldr, loc, box);808 });809 resultIsFinalized = true;810 }811 }812 }813 return {LoweredResult{*allocatedResult}, resultIsFinalized};814 }815 816 // subroutine call817 if (!resultType)818 return {LoweredResult{fir::ExtendedValue{mlir::Value{}}},819 /*resultIsFinalized=*/false};820 821 // For now, Fortran return values are implemented with a single MLIR822 // function return value.823 assert(callNumResults == 1 && "Expected exactly one result in FUNCTION call");824 (void)callNumResults;825 826 // Call a BIND(C) function that return a char.827 if (caller.characterize().IsBindC() &&828 mlir::isa<fir::CharacterType>(funcType.getResults()[0])) {829 fir::CharacterType charTy =830 mlir::dyn_cast<fir::CharacterType>(funcType.getResults()[0]);831 mlir::Value len = builder.createIntegerConstant(832 loc, builder.getCharacterLengthType(), charTy.getLen());833 return {834 LoweredResult{fir::ExtendedValue{fir::CharBoxValue{callResult, len}}},835 /*resultIsFinalized=*/false};836 }837 838 return {LoweredResult{fir::ExtendedValue{callResult}},839 /*resultIsFinalized=*/false};840}841 842static hlfir::EntityWithAttributes genStmtFunctionRef(843 mlir::Location loc, Fortran::lower::AbstractConverter &converter,844 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,845 const Fortran::evaluate::ProcedureRef &procRef) {846 const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol();847 assert(symbol && "expected symbol in ProcedureRef of statement functions");848 const auto &details = symbol->get<Fortran::semantics::SubprogramDetails>();849 fir::FirOpBuilder &builder = converter.getFirOpBuilder();850 851 // Statement functions have their own scope, we just need to associate852 // the dummy symbols to argument expressions. There are no853 // optional/alternate return arguments. Statement functions cannot be854 // recursive (directly or indirectly) so it is safe to add dummy symbols to855 // the local map here.856 symMap.pushScope();857 llvm::SmallVector<hlfir::AssociateOp> exprAssociations;858 for (auto [arg, bind] : llvm::zip(details.dummyArgs(), procRef.arguments())) {859 assert(arg && "alternate return in statement function");860 assert(bind && "optional argument in statement function");861 const auto *expr = bind->UnwrapExpr();862 // TODO: assumed type in statement function, that surprisingly seems863 // allowed, probably because nobody thought of restricting this usage.864 // gfortran/ifort compiles this.865 assert(expr && "assumed type used as statement function argument");866 // As per Fortran 2018 C1580, statement function arguments can only be867 // scalars.868 // The only care is to use the dummy character explicit length if any869 // instead of the actual argument length (that can be bigger).870 hlfir::EntityWithAttributes loweredArg = Fortran::lower::convertExprToHLFIR(871 loc, converter, *expr, symMap, stmtCtx);872 fir::FortranVariableOpInterface variableIface = loweredArg.getIfVariable();873 if (!variableIface) {874 // So far only FortranVariableOpInterface can be mapped to symbols.875 // Create an hlfir.associate to create a variable from a potential876 // value argument.877 mlir::Type argType = converter.genType(*arg);878 auto associate = hlfir::genAssociateExpr(879 loc, builder, loweredArg, argType, toStringRef(arg->name()));880 exprAssociations.push_back(associate);881 variableIface = associate;882 }883 const Fortran::semantics::DeclTypeSpec *type = arg->GetType();884 if (type &&885 type->category() == Fortran::semantics::DeclTypeSpec::Character) {886 // Instantiate character as if it was a normal dummy argument so that the887 // statement function dummy character length is applied and dealt with888 // correctly.889 symMap.addSymbol(*arg, variableIface.getBase());890 Fortran::lower::mapSymbolAttributes(converter, *arg, symMap, stmtCtx);891 } else {892 // No need to create an extra hlfir.declare otherwise for893 // numerical and logical scalar dummies.894 symMap.addVariableDefinition(*arg, variableIface);895 }896 }897 898 // Explicitly map statement function host associated symbols to their899 // parent scope lowered symbol box.900 for (const Fortran::semantics::SymbolRef &sym :901 Fortran::evaluate::CollectSymbols(*details.stmtFunction()))902 if (const auto *details =903 sym->detailsIf<Fortran::semantics::HostAssocDetails>())904 converter.copySymbolBinding(details->symbol(), sym);905 906 hlfir::Entity result = Fortran::lower::convertExprToHLFIR(907 loc, converter, details.stmtFunction().value(), symMap, stmtCtx);908 symMap.popScope();909 // The result must not be a variable.910 result = hlfir::loadTrivialScalar(loc, builder, result);911 if (result.isVariable())912 result = hlfir::Entity{hlfir::AsExprOp::create(builder, loc, result)};913 for (auto associate : exprAssociations)914 hlfir::EndAssociateOp::create(builder, loc, associate);915 return hlfir::EntityWithAttributes{result};916}917 918namespace {919// Structure to hold the information about the call and the lowering context.920// This structure is intended to help threading the information921// through the various lowering calls without having to pass every922// required structure one by one.923struct CallContext {924 CallContext(const Fortran::evaluate::ProcedureRef &procRef,925 std::optional<mlir::Type> resultType, mlir::Location loc,926 Fortran::lower::AbstractConverter &converter,927 Fortran::lower::SymMap &symMap,928 Fortran::lower::StatementContext &stmtCtx, bool doCopyIn = true)929 : procRef{procRef}, converter{converter}, symMap{symMap},930 stmtCtx{stmtCtx}, resultType{resultType}, loc{loc}, doCopyIn{doCopyIn} {931 }932 933 fir::FirOpBuilder &getBuilder() { return converter.getFirOpBuilder(); }934 935 std::string getProcedureName() const {936 if (const Fortran::semantics::Symbol *sym = procRef.proc().GetSymbol())937 return sym->GetUltimate().name().ToString();938 return procRef.proc().GetName();939 }940 941 /// Is this a call to an elemental procedure with at least one array argument?942 bool isElementalProcWithArrayArgs() const {943 if (procRef.IsElemental())944 for (const std::optional<Fortran::evaluate::ActualArgument> &arg :945 procRef.arguments())946 if (arg && arg->Rank() != 0)947 return true;948 return false;949 }950 951 /// Is this a statement function reference?952 bool isStatementFunctionCall() const {953 if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())954 if (const auto *details =955 symbol->detailsIf<Fortran::semantics::SubprogramDetails>())956 return details->stmtFunction().has_value();957 return false;958 }959 960 /// Is this a call to a BIND(C) procedure?961 bool isBindcCall() const {962 if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())963 return Fortran::semantics::IsBindCProcedure(*symbol);964 return false;965 }966 967 const Fortran::evaluate::ProcedureRef &procRef;968 Fortran::lower::AbstractConverter &converter;969 Fortran::lower::SymMap &symMap;970 Fortran::lower::StatementContext &stmtCtx;971 std::optional<mlir::Type> resultType;972 mlir::Location loc;973 bool doCopyIn;974};975 976using ExvAndCleanup =977 std::pair<fir::ExtendedValue, std::optional<hlfir::CleanupFunction>>;978} // namespace979 980// Helper to transform a fir::ExtendedValue to an hlfir::EntityWithAttributes.981static hlfir::EntityWithAttributes982extendedValueToHlfirEntity(mlir::Location loc, fir::FirOpBuilder &builder,983 const fir::ExtendedValue &exv,984 llvm::StringRef name) {985 mlir::Value firBase = fir::getBase(exv);986 mlir::Type firBaseTy = firBase.getType();987 if (fir::isa_trivial(firBaseTy))988 return hlfir::EntityWithAttributes{firBase};989 if (auto charTy = mlir::dyn_cast<fir::CharacterType>(firBase.getType())) {990 // CHAR() intrinsic and BIND(C) procedures returning CHARACTER(1)991 // are lowered to a fir.char<kind,1> that is not in memory.992 // This tends to cause a lot of bugs because the rest of the993 // infrastructure is mostly tested with characters that are994 // in memory.995 // To avoid having to deal with this special case here and there,996 // place it in memory here. If this turns out to be suboptimal,997 // this could be fixed, but for now llvm opt -O1 is able to get998 // rid of the memory indirection in a = char(b), so there is999 // little incentive to increase the compiler complexity.1000 hlfir::Entity storage{builder.createTemporary(loc, charTy)};1001 fir::StoreOp::create(builder, loc, firBase, storage);1002 auto asExpr = hlfir::AsExprOp::create(1003 builder, loc, storage, /*mustFree=*/builder.createBool(loc, false));1004 return hlfir::EntityWithAttributes{asExpr.getResult()};1005 }1006 return hlfir::genDeclare(loc, builder, exv, name,1007 fir::FortranVariableFlagsAttr{});1008}1009namespace {1010/// Structure to hold the clean-up related to a dummy argument preparation1011/// that may have to be done after a call (copy-out or temporary deallocation).1012struct CallCleanUp {1013 struct CopyIn {1014 void genCleanUp(mlir::Location loc, fir::FirOpBuilder &builder) {1015 hlfir::CopyOutOp::create(builder, loc, tempBox, wasCopied, copyBackVar);1016 }1017 // address of the descriptor holding the temp if a temp was created.1018 mlir::Value tempBox;1019 // Boolean indicating if a copy was made or not.1020 mlir::Value wasCopied;1021 // copyBackVar may be null if copy back is not needed.1022 mlir::Value copyBackVar;1023 };1024 struct ExprAssociate {1025 void genCleanUp(mlir::Location loc, fir::FirOpBuilder &builder) {1026 hlfir::EndAssociateOp::create(builder, loc, tempVar, mustFree);1027 }1028 mlir::Value tempVar;1029 mlir::Value mustFree;1030 };1031 1032 /// Generate clean-up code.1033 /// If \p postponeAssociates is true, the ExprAssociate clean-up1034 /// is not generated, and instead the corresponding CallCleanUp1035 /// object is returned as the result.1036 std::optional<CallCleanUp> genCleanUp(mlir::Location loc,1037 fir::FirOpBuilder &builder,1038 bool postponeAssociates) {1039 std::optional<CallCleanUp> postponed;1040 Fortran::common::visit(Fortran::common::visitors{1041 [&](CopyIn &c) { c.genCleanUp(loc, builder); },1042 [&](ExprAssociate &c) {1043 if (postponeAssociates)1044 postponed = CallCleanUp{c};1045 else1046 c.genCleanUp(loc, builder);1047 },1048 },1049 cleanUp);1050 return postponed;1051 }1052 std::variant<CopyIn, ExprAssociate> cleanUp;1053};1054 1055/// Structure representing a prepared dummy argument.1056/// It holds the value to be passed in the call and any related1057/// clean-ups to be done after the call.1058struct PreparedDummyArgument {1059 void pushCopyInCleanUp(mlir::Value tempBox, mlir::Value wasCopied,1060 mlir::Value copyBackVar) {1061 cleanups.emplace_back(1062 CallCleanUp{CallCleanUp::CopyIn{tempBox, wasCopied, copyBackVar}});1063 }1064 void pushExprAssociateCleanUp(mlir::Value tempVar, mlir::Value wasCopied) {1065 cleanups.emplace_back(1066 CallCleanUp{CallCleanUp::ExprAssociate{tempVar, wasCopied}});1067 }1068 void pushExprAssociateCleanUp(hlfir::AssociateOp associate) {1069 mlir::Value hlfirBase = associate.getBase();1070 mlir::Value firBase = associate.getFirBase();1071 cleanups.emplace_back(CallCleanUp{CallCleanUp::ExprAssociate{1072 hlfir::mayHaveAllocatableComponent(hlfirBase.getType()) ? hlfirBase1073 : firBase,1074 associate.getMustFreeStrorageFlag()}});1075 }1076 1077 mlir::Value dummy;1078 // NOTE: the clean-ups are executed in reverse order.1079 llvm::SmallVector<CallCleanUp, 2> cleanups;1080};1081 1082/// Structure to help conditionally preparing a dummy argument based1083/// on the actual argument presence.1084/// It helps "wrapping" the dummy and the clean-up information in1085/// an if (present) {...}:1086///1087/// %conditionallyPrepared = fir.if (%present) {1088/// fir.result %preparedDummy1089/// } else {1090/// fir.result %absent1091/// }1092///1093struct ConditionallyPreparedDummy {1094 /// Create ConditionallyPreparedDummy from a preparedDummy that must1095 /// be wrapped in a fir.if.1096 ConditionallyPreparedDummy(PreparedDummyArgument &preparedDummy) {1097 thenResultValues.push_back(preparedDummy.dummy);1098 for (const CallCleanUp &c : preparedDummy.cleanups) {1099 if (const auto *copyInCleanUp =1100 std::get_if<CallCleanUp::CopyIn>(&c.cleanUp)) {1101 thenResultValues.push_back(copyInCleanUp->wasCopied);1102 if (copyInCleanUp->copyBackVar)1103 thenResultValues.push_back(copyInCleanUp->copyBackVar);1104 } else {1105 const auto &exprAssociate =1106 std::get<CallCleanUp::ExprAssociate>(c.cleanUp);1107 thenResultValues.push_back(exprAssociate.tempVar);1108 thenResultValues.push_back(exprAssociate.mustFree);1109 }1110 }1111 }1112 1113 /// Get the result types of the wrapping fir.if that must be created.1114 llvm::SmallVector<mlir::Type> getIfResulTypes() const {1115 llvm::SmallVector<mlir::Type> types;1116 for (mlir::Value res : thenResultValues)1117 types.push_back(res.getType());1118 return types;1119 }1120 1121 /// Generate the "fir.result %preparedDummy" in the then branch of the1122 /// wrapping fir.if.1123 void genThenResult(mlir::Location loc, fir::FirOpBuilder &builder) const {1124 fir::ResultOp::create(builder, loc, thenResultValues);1125 }1126 1127 /// Generate the "fir.result %absent" in the else branch of the1128 /// wrapping fir.if.1129 void genElseResult(mlir::Location loc, fir::FirOpBuilder &builder) const {1130 llvm::SmallVector<mlir::Value> elseResultValues;1131 mlir::Type i1Type = builder.getI1Type();1132 for (mlir::Value res : thenResultValues) {1133 mlir::Type type = res.getType();1134 if (type == i1Type)1135 elseResultValues.push_back(builder.createBool(loc, false));1136 else1137 elseResultValues.push_back(builder.genAbsentOp(loc, type));1138 }1139 fir::ResultOp::create(builder, loc, elseResultValues);1140 }1141 1142 /// Once the fir.if has been created, get the resulting %conditionallyPrepared1143 /// dummy argument.1144 PreparedDummyArgument1145 getPreparedDummy(fir::IfOp ifOp,1146 const PreparedDummyArgument &unconditionalDummy) {1147 PreparedDummyArgument preparedDummy;1148 preparedDummy.dummy = ifOp.getResults()[0];1149 for (const CallCleanUp &c : unconditionalDummy.cleanups) {1150 if (const auto *copyInCleanUp =1151 std::get_if<CallCleanUp::CopyIn>(&c.cleanUp)) {1152 mlir::Value copyBackVar;1153 if (copyInCleanUp->copyBackVar)1154 copyBackVar = ifOp.getResults().back();1155 // tempBox is an hlfir.copy_in argument created outside of the1156 // fir.if region. It needs not to be threaded as a fir.if result.1157 preparedDummy.pushCopyInCleanUp(copyInCleanUp->tempBox,1158 ifOp.getResults()[1], copyBackVar);1159 } else {1160 preparedDummy.pushExprAssociateCleanUp(ifOp.getResults()[1],1161 ifOp.getResults()[2]);1162 }1163 }1164 return preparedDummy;1165 }1166 1167 llvm::SmallVector<mlir::Value> thenResultValues;1168};1169} // namespace1170 1171/// Fix-up the fact that it is supported to pass a character procedure1172/// designator to a non character procedure dummy procedure and vice-versa, even1173/// in case of explicit interface. Uglier cases where an object is passed as1174/// procedure designator or vice versa are handled only for implicit interfaces1175/// (refused by semantics with explicit interface), and handled with a funcOp1176/// cast like other implicit interface mismatches.1177static hlfir::Entity fixProcedureDummyMismatch(mlir::Location loc,1178 fir::FirOpBuilder &builder,1179 hlfir::Entity actual,1180 mlir::Type dummyType) {1181 if (mlir::isa<fir::BoxProcType>(actual.getType()) &&1182 fir::isCharacterProcedureTuple(dummyType)) {1183 mlir::Value length =1184 fir::UndefOp::create(builder, loc, builder.getCharacterLengthType());1185 mlir::Value tuple = fir::factory::createCharacterProcedureTuple(1186 builder, loc, dummyType, actual, length);1187 return hlfir::Entity{tuple};1188 }1189 assert(fir::isCharacterProcedureTuple(actual.getType()) &&1190 mlir::isa<fir::BoxProcType>(dummyType) &&1191 "unsupported dummy procedure mismatch with the actual argument");1192 mlir::Value boxProc = fir::factory::extractCharacterProcedureTuple(1193 builder, loc, actual, /*openBoxProc=*/false)1194 .first;1195 return hlfir::Entity{boxProc};1196}1197 1198mlir::Value static getZeroLowerBounds(mlir::Location loc,1199 fir::FirOpBuilder &builder,1200 hlfir::Entity entity) {1201 assert(!entity.isAssumedRank() &&1202 "assumed-rank must use fir.rebox_assumed_rank");1203 if (entity.getRank() < 1)1204 return {};1205 mlir::Value zero =1206 builder.createIntegerConstant(loc, builder.getIndexType(), 0);1207 llvm::SmallVector<mlir::Value> lowerBounds(entity.getRank(), zero);1208 return builder.genShift(loc, lowerBounds);1209}1210 1211static bool isParameterObjectOrSubObject(hlfir::Entity entity) {1212 mlir::Value base = entity;1213 bool foundParameter = false;1214 while (mlir::Operation *op = base ? base.getDefiningOp() : nullptr) {1215 base =1216 llvm::TypeSwitch<mlir::Operation *, mlir::Value>(op)1217 .Case<hlfir::DeclareOp>([&](auto declare) -> mlir::Value {1218 foundParameter |= hlfir::Entity{declare}.isParameter();1219 return foundParameter ? mlir::Value{} : declare.getMemref();1220 })1221 .Case<hlfir::DesignateOp, hlfir::ParentComponentOp, fir::EmboxOp>(1222 [&](auto op) -> mlir::Value { return op.getMemref(); })1223 .Case<fir::ReboxOp>(1224 [&](auto rebox) -> mlir::Value { return rebox.getBox(); })1225 .Case<fir::ConvertOp>(1226 [&](auto convert) -> mlir::Value { return convert.getValue(); })1227 .Default([](mlir::Operation *) -> mlir::Value { return nullptr; });1228 }1229 return foundParameter;1230}1231 1232/// When dummy is not ALLOCATABLE, POINTER and is not passed in register,1233/// prepare the actual argument according to the interface. Do as needed:1234/// - address element if this is an array argument in an elemental call.1235/// - set dynamic type to the dummy type if the dummy is not polymorphic.1236/// - copy-in into contiguous variable if the dummy must be contiguous1237/// - copy into a temporary if the dummy has the VALUE attribute.1238/// - package the prepared dummy as required (fir.box, fir.class,1239/// fir.box_char...).1240/// This function should only be called with an actual that is present.1241/// The optional aspects must be handled by this function user.1242///1243/// Note: while Fortran::lower::CallerInterface::PassedEntity (the type of arg)1244/// is technically a template type, in the prepare*ActualArgument() calls1245/// it resolves to Fortran::evaluate::ActualArgument *1246static PreparedDummyArgument preparePresentUserCallActualArgument(1247 mlir::Location loc, fir::FirOpBuilder &builder,1248 const Fortran::lower::PreparedActualArgument &preparedActual,1249 mlir::Type dummyType,1250 const Fortran::lower::CallerInterface::PassedEntity &arg,1251 CallContext &callContext) {1252 1253 // Step 1: get the actual argument, which includes addressing the1254 // element if this is an array in an elemental call.1255 hlfir::Entity actual = preparedActual.getActual(loc, builder);1256 1257 // Handle procedure arguments (procedure pointers should go through1258 // prepareProcedurePointerActualArgument).1259 if (hlfir::isFortranProcedureValue(dummyType)) {1260 // Procedure pointer or function returns procedure pointer actual to1261 // procedure dummy.1262 if (actual.isProcedurePointer()) {1263 actual = hlfir::derefPointersAndAllocatables(loc, builder, actual);1264 return PreparedDummyArgument{actual, /*cleanups=*/{}};1265 }1266 // Procedure actual to procedure dummy.1267 assert(actual.isProcedure());1268 // Do nothing if this is a procedure argument. It is already a1269 // fir.boxproc/fir.tuple<fir.boxproc, len> as it should.1270 if (!mlir::isa<fir::BoxProcType>(actual.getType()) &&1271 actual.getType() != dummyType)1272 // The actual argument may be a procedure that returns character (a1273 // fir.tuple<fir.boxproc, len>) while the dummy is not. Extract the tuple1274 // in that case.1275 actual = fixProcedureDummyMismatch(loc, builder, actual, dummyType);1276 return PreparedDummyArgument{actual, /*cleanups=*/{}};1277 }1278 1279 const bool ignoreTKRtype = arg.testTKR(Fortran::common::IgnoreTKR::Type);1280 const bool passingPolymorphicToNonPolymorphic =1281 actual.isPolymorphic() && !fir::isPolymorphicType(dummyType) &&1282 !ignoreTKRtype;1283 1284 // When passing a CLASS(T) to TYPE(T), only the "T" part must be1285 // passed. Unless the entity is a scalar passed by raw address, a1286 // new descriptor must be made using the dummy argument type as1287 // dynamic type. This must be done before any copy/copy-in because the1288 // dynamic type matters to determine the contiguity.1289 const bool mustSetDynamicTypeToDummyType =1290 passingPolymorphicToNonPolymorphic &&1291 (actual.isArray() || mlir::isa<fir::BaseBoxType>(dummyType));1292 1293 bool mustDoCopyIn{false};1294 bool mustDoCopyOut{false};1295 1296 if (callContext.doCopyIn) {1297 Fortran::evaluate::FoldingContext &foldingContext{1298 callContext.converter.getFoldingContext()};1299 1300 bool suggestCopyIn = Fortran::evaluate::ActualArgNeedsCopy(1301 arg.entity, arg.characteristics, foldingContext,1302 /*forCopyOut=*/false)1303 .value_or(true);1304 bool suggestCopyOut = Fortran::evaluate::ActualArgNeedsCopy(1305 arg.entity, arg.characteristics, foldingContext,1306 /*forCopyOut=*/true)1307 .value_or(true);1308 mustDoCopyIn = actual.isArray() && suggestCopyIn;1309 mustDoCopyOut = actual.isArray() && suggestCopyOut;1310 }1311 1312 const bool actualIsAssumedRank = actual.isAssumedRank();1313 // Create dummy type with actual argument rank when the dummy is an assumed1314 // rank. That way, all the operation to create dummy descriptors are ranked if1315 // the actual argument is ranked, which allows simple code generation.1316 // Also do the same when the dummy is a sequence associated descriptor1317 // because the actual shape/rank may mismatch with the dummy, and the dummy1318 // may be an assumed-size array, so any descriptor manipulation should use the1319 // actual argument shape information. A descriptor with the dummy shape1320 // information will be created later when all actual arguments are ready.1321 mlir::Type dummyTypeWithActualRank = dummyType;1322 if (auto baseBoxDummy = mlir::dyn_cast<fir::BaseBoxType>(dummyType)) {1323 if (baseBoxDummy.isAssumedRank() ||1324 arg.testTKR(Fortran::common::IgnoreTKR::Rank) ||1325 arg.isSequenceAssociatedDescriptor()) {1326 mlir::Type actualTy =1327 hlfir::getFortranElementOrSequenceType(actual.getType());1328 dummyTypeWithActualRank = baseBoxDummy.getBoxTypeWithNewShape(actualTy);1329 }1330 }1331 // Preserve the actual type in the argument preparation in case IgnoreTKR(t)1332 // is set (descriptors must be created with the actual type in this case, and1333 // copy-in/copy-out should be driven by the contiguity with regard to the1334 // actual type).1335 if (ignoreTKRtype) {1336 if (auto boxCharType =1337 mlir::dyn_cast<fir::BoxCharType>(dummyTypeWithActualRank)) {1338 auto maybeActualCharType =1339 mlir::dyn_cast<fir::CharacterType>(actual.getFortranElementType());1340 if (!maybeActualCharType ||1341 maybeActualCharType.getFKind() != boxCharType.getKind()) {1342 // When passing to a fir.boxchar with ignore(tk), prepare the argument1343 // as if only the raw address must be passed.1344 dummyTypeWithActualRank =1345 fir::ReferenceType::get(actual.getElementOrSequenceType());1346 }1347 // Otherwise, the actual is already a character with the same kind as the1348 // dummy and can be passed normally.1349 } else {1350 dummyTypeWithActualRank = fir::changeElementType(1351 dummyTypeWithActualRank, actual.getFortranElementType(),1352 actual.isPolymorphic());1353 }1354 }1355 1356 PreparedDummyArgument preparedDummy;1357 1358 // Helpers to generate hlfir.copy_in operation and register the related1359 // hlfir.copy_out creation.1360 auto genCopyIn = [&](hlfir::Entity var, bool doCopyOut) -> hlfir::Entity {1361 auto baseBoxTy = mlir::dyn_cast<fir::BaseBoxType>(var.getType());1362 assert(baseBoxTy && "expect non simply contiguous variables to be boxes");1363 // Create allocatable descriptor for the potential temporary.1364 mlir::Type tempBoxType = baseBoxTy.getBoxTypeWithNewAttr(1365 fir::BaseBoxType::Attribute::Allocatable);1366 mlir::Value tempBox = builder.createTemporary(loc, tempBoxType);1367 auto copyIn = hlfir::CopyInOp::create(builder, loc, var, tempBox,1368 /*var_is_present=*/mlir::Value{});1369 // Register the copy-out after the call.1370 preparedDummy.pushCopyInCleanUp(copyIn.getTempBox(), copyIn.getWasCopied(),1371 doCopyOut ? copyIn.getVar()1372 : mlir::Value{});1373 return hlfir::Entity{copyIn.getCopiedIn()};1374 };1375 1376 auto genSetDynamicTypeToDummyType = [&](hlfir::Entity var) -> hlfir::Entity {1377 fir::BaseBoxType boxType = fir::BoxType::get(1378 hlfir::getFortranElementOrSequenceType(dummyTypeWithActualRank));1379 if (actualIsAssumedRank)1380 return hlfir::Entity{fir::ReboxAssumedRankOp::create(1381 builder, loc, boxType, var,1382 fir::LowerBoundModifierAttribute::SetToOnes)};1383 // Use actual shape when creating descriptor with dummy type, the dummy1384 // shape may be unknown in case of sequence association.1385 mlir::Type actualTy =1386 hlfir::getFortranElementOrSequenceType(actual.getType());1387 boxType = boxType.getBoxTypeWithNewShape(actualTy);1388 return hlfir::Entity{fir::ReboxOp::create(builder, loc, boxType, var,1389 /*shape=*/mlir::Value{},1390 /*slice=*/mlir::Value{})};1391 };1392 1393 // Step 2: prepare the storage for the dummy arguments, ensuring that it1394 // matches the dummy requirements (e.g., must be contiguous or must be1395 // a temporary).1396 hlfir::Entity entity =1397 hlfir::derefPointersAndAllocatables(loc, builder, actual);1398 if (entity.isVariable()) {1399 // Set dynamic type if needed before any copy-in or copy so that the dummy1400 // is contiguous according to the dummy type.1401 if (mustSetDynamicTypeToDummyType)1402 entity = genSetDynamicTypeToDummyType(entity);1403 if (arg.hasValueAttribute() ||1404 // Constant expressions might be lowered as variables with1405 // 'parameter' attribute. Even though the constant expressions1406 // are not definable and explicit assignments to them are not1407 // possible, we have to create a temporary copies when we pass1408 // them down the call stack because of potential compiler1409 // generated writes in copy-out.1410 isParameterObjectOrSubObject(entity)) {1411 // Make a copy in a temporary.1412 auto copy = hlfir::AsExprOp::create(builder, loc, entity);1413 mlir::Type storageType = entity.getType();1414 mlir::NamedAttribute byRefAttr = fir::getAdaptToByRefAttr(builder);1415 hlfir::AssociateOp associate = hlfir::genAssociateExpr(1416 loc, builder, hlfir::Entity{copy}, storageType, "", byRefAttr);1417 entity = hlfir::Entity{associate.getBase()};1418 // Register the temporary destruction after the call.1419 preparedDummy.pushExprAssociateCleanUp(associate);1420 } else if (mustDoCopyIn || mustDoCopyOut) {1421 // Copy-in non contiguous variables.1422 //1423 // TODO: copy-in and copy-out are now determined separately, in order1424 // to allow more fine grained copying. While currently both copy-in1425 // and copy-out are must be done together, these copy operations could1426 // be separated in the future. (This is related to TODO comment below.)1427 //1428 // TODO: for non-finalizable monomorphic derived type actual1429 // arguments associated with INTENT(OUT) dummy arguments1430 // we may avoid doing the copy and only allocate the temporary.1431 // The codegen would do a "mold" allocation instead of "sourced"1432 // allocation for the temp in this case. We can communicate1433 // this to the codegen via some CopyInOp flag.1434 // This is a performance concern.1435 entity = genCopyIn(entity, mustDoCopyOut);1436 }1437 } else {1438 const Fortran::lower::SomeExpr *expr = arg.entity->UnwrapExpr();1439 assert(expr && "expression actual argument cannot be an assumed type");1440 // The actual is an expression value, place it into a temporary1441 // and register the temporary destruction after the call.1442 mlir::Type storageType = callContext.converter.genType(*expr);1443 mlir::NamedAttribute byRefAttr = fir::getAdaptToByRefAttr(builder);1444 hlfir::AssociateOp associate = hlfir::genAssociateExpr(1445 loc, builder, entity, storageType, "", byRefAttr);1446 entity = hlfir::Entity{associate.getBase()};1447 preparedDummy.pushExprAssociateCleanUp(associate);1448 // Rebox the actual argument to the dummy argument's type, and make sure1449 // that we pass a contiguous entity (i.e. make copy-in, if needed).1450 //1451 // TODO: this can probably be optimized by associating the expression with1452 // properly typed temporary, but this needs either a new operation or1453 // making the hlfir.associate more complex.1454 if (mustSetDynamicTypeToDummyType) {1455 entity = genSetDynamicTypeToDummyType(entity);1456 entity = genCopyIn(entity, /*doCopyOut=*/false);1457 }1458 }1459 1460 // Step 3: now that the dummy argument storage has been prepared, package1461 // it according to the interface.1462 mlir::Value addr;1463 if (mlir::isa<fir::BoxCharType>(dummyTypeWithActualRank)) {1464 // Cast the argument to match the volatility of the dummy argument.1465 auto nonVolatileEntity = hlfir::Entity{builder.createVolatileCast(1466 loc, fir::isa_volatile_type(dummyType), entity)};1467 addr = hlfir::genVariableBoxChar(loc, builder, nonVolatileEntity);1468 } else if (mlir::isa<fir::BaseBoxType>(dummyTypeWithActualRank)) {1469 entity = hlfir::genVariableBox(loc, builder, entity);1470 // Ensures the box has the right attributes and that it holds an1471 // addendum if needed.1472 fir::BaseBoxType actualBoxType =1473 mlir::cast<fir::BaseBoxType>(entity.getType());1474 mlir::Type boxEleType = actualBoxType.getEleTy();1475 // For now, assume it is not OK to pass the allocatable/pointer1476 // descriptor to a non pointer/allocatable dummy. That is a strict1477 // interpretation of 18.3.6 point 4 that stipulates the descriptor1478 // has the dummy attributes in BIND(C) contexts.1479 const bool actualBoxHasAllocatableOrPointerFlag =1480 fir::isa_ref_type(boxEleType);1481 // Fortran 2018 18.5.3, pp3: BIND(C) non pointer allocatable descriptors1482 // must have zero lower bounds.1483 bool needsZeroLowerBounds = callContext.isBindcCall() && entity.isArray();1484 // On the callee side, the current code generated for unlimited1485 // polymorphic might unconditionally read the addendum. Intrinsic type1486 // descriptors may not have an addendum, the rebox below will create a1487 // descriptor with an addendum in such case.1488 const bool actualBoxHasAddendum = fir::boxHasAddendum(actualBoxType);1489 const bool needToAddAddendum =1490 fir::isUnlimitedPolymorphicType(dummyTypeWithActualRank) &&1491 !actualBoxHasAddendum;1492 if (needToAddAddendum || actualBoxHasAllocatableOrPointerFlag ||1493 needsZeroLowerBounds) {1494 if (actualIsAssumedRank) {1495 auto lbModifier = needsZeroLowerBounds1496 ? fir::LowerBoundModifierAttribute::SetToZeroes1497 : fir::LowerBoundModifierAttribute::SetToOnes;1498 entity = hlfir::Entity{fir::ReboxAssumedRankOp::create(1499 builder, loc, dummyTypeWithActualRank, entity, lbModifier)};1500 } else {1501 mlir::Value shift{};1502 if (needsZeroLowerBounds)1503 shift = getZeroLowerBounds(loc, builder, entity);1504 entity = hlfir::Entity{fir::ReboxOp::create(1505 builder, loc, dummyTypeWithActualRank, entity, /*shape=*/shift,1506 /*slice=*/mlir::Value{})};1507 }1508 }1509 addr = entity;1510 } else {1511 addr = hlfir::genVariableRawAddress(loc, builder, entity);1512 }1513 1514 // If the volatility of the input type does not match the dummy type,1515 // we need to cast the argument.1516 const bool isToTypeVolatile = fir::isa_volatile_type(dummyTypeWithActualRank);1517 addr = builder.createVolatileCast(loc, isToTypeVolatile, addr);1518 1519 // For ranked actual passed to assumed-rank dummy, the cast to assumed-rank1520 // box is inserted when building the fir.call op. Inserting it here would1521 // cause the fir.if results to be assumed-rank in case of OPTIONAL dummy,1522 // causing extra runtime costs due to the unknown runtime size of assumed-rank1523 // descriptors.1524 // For TKR dummy characters, the boxchar creation also happens later when1525 // creating the fir.call .1526 preparedDummy.dummy =1527 builder.createConvert(loc, dummyTypeWithActualRank, addr);1528 return preparedDummy;1529}1530 1531/// When dummy is not ALLOCATABLE, POINTER and is not passed in register,1532/// prepare the actual argument according to the interface, taking care1533/// of any optional aspect.1534static PreparedDummyArgument prepareUserCallActualArgument(1535 mlir::Location loc, fir::FirOpBuilder &builder,1536 const Fortran::lower::PreparedActualArgument &preparedActual,1537 mlir::Type dummyType,1538 const Fortran::lower::CallerInterface::PassedEntity &arg,1539 CallContext &callContext) {1540 if (!preparedActual.handleDynamicOptional())1541 return preparePresentUserCallActualArgument(loc, builder, preparedActual,1542 dummyType, arg, callContext);1543 1544 // Conditional dummy argument preparation. The actual may be absent1545 // at runtime, causing any addressing, copy, and packaging to have1546 // undefined behavior.1547 // To simplify the handling of this case, the "normal" dummy preparation1548 // helper is used, except its generated code is wrapped inside a1549 // fir.if(present).1550 mlir::Value isPresent = preparedActual.getIsPresent();1551 mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint();1552 1553 // Code generated in a preparation block that will become the1554 // "then" block in "if (present) then {} else {}". The reason1555 // for this unusual if/then/else generation is that the number1556 // and types of the if results will depend on how the argument1557 // is prepared, and forecasting that here would be brittle.1558 auto badIfOp = fir::IfOp::create(builder, loc, dummyType, isPresent,1559 /*withElseRegion=*/false);1560 mlir::Block *preparationBlock = &badIfOp.getThenRegion().front();1561 builder.setInsertionPointToStart(preparationBlock);1562 PreparedDummyArgument unconditionalDummy =1563 preparePresentUserCallActualArgument(loc, builder, preparedActual,1564 dummyType, arg, callContext);1565 builder.restoreInsertionPoint(insertPt);1566 1567 // TODO: when forwarding an optional to an optional of the same kind1568 // (i.e, unconditionalDummy.dummy was not created in preparationBlock),1569 // the if/then/else generation could be skipped to improve the generated1570 // code.1571 1572 // Now that the result types of the ifOp can be deduced, generate1573 // the "real" ifOp (operation result types cannot be changed, so1574 // badIfOp cannot be modified and used here).1575 llvm::SmallVector<mlir::Type> ifOpResultTypes;1576 ConditionallyPreparedDummy conditionalDummy(unconditionalDummy);1577 auto ifOp = fir::IfOp::create(builder, loc,1578 conditionalDummy.getIfResulTypes(), isPresent,1579 /*withElseRegion=*/true);1580 // Move "preparationBlock" into the "then" of the new1581 // fir.if operation and create fir.result propagating1582 // unconditionalDummy.1583 preparationBlock->moveBefore(&ifOp.getThenRegion().back());1584 ifOp.getThenRegion().back().erase();1585 builder.setInsertionPointToEnd(&ifOp.getThenRegion().front());1586 conditionalDummy.genThenResult(loc, builder);1587 1588 // Generate "else" branch with returning absent values.1589 builder.setInsertionPointToStart(&ifOp.getElseRegion().front());1590 conditionalDummy.genElseResult(loc, builder);1591 1592 // Build dummy from IfOpResults.1593 builder.setInsertionPointAfter(ifOp);1594 PreparedDummyArgument result =1595 conditionalDummy.getPreparedDummy(ifOp, unconditionalDummy);1596 badIfOp->erase();1597 return result;1598}1599 1600/// Prepare actual argument for a procedure pointer dummy.1601static PreparedDummyArgument prepareProcedurePointerActualArgument(1602 mlir::Location loc, fir::FirOpBuilder &builder,1603 const Fortran::lower::PreparedActualArgument &preparedActual,1604 mlir::Type dummyType,1605 const Fortran::lower::CallerInterface::PassedEntity &arg,1606 CallContext &callContext) {1607 1608 // NULL() actual to procedure pointer dummy1609 if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(1610 *arg.entity) &&1611 fir::isBoxProcAddressType(dummyType)) {1612 auto boxTy{Fortran::lower::getUntypedBoxProcType(builder.getContext())};1613 auto tempBoxProc{builder.createTemporary(loc, boxTy)};1614 hlfir::Entity nullBoxProc(1615 fir::factory::createNullBoxProc(builder, loc, boxTy));1616 fir::StoreOp::create(builder, loc, nullBoxProc, tempBoxProc);1617 return PreparedDummyArgument{tempBoxProc, /*cleanups=*/{}};1618 }1619 hlfir::Entity actual = preparedActual.getActual(loc, builder);1620 if (actual.isProcedurePointer())1621 return PreparedDummyArgument{actual, /*cleanups=*/{}};1622 assert(actual.isProcedure());1623 // Procedure actual to procedure pointer dummy.1624 auto tempBoxProc{builder.createTemporary(loc, actual.getType())};1625 fir::StoreOp::create(builder, loc, actual, tempBoxProc);1626 return PreparedDummyArgument{tempBoxProc, /*cleanups=*/{}};1627}1628 1629/// Prepare arguments of calls to user procedures with actual arguments that1630/// have been pre-lowered but not yet prepared according to the interface.1631void prepareUserCallArguments(1632 Fortran::lower::PreparedActualArguments &loweredActuals,1633 Fortran::lower::CallerInterface &caller, mlir::FunctionType callSiteType,1634 CallContext &callContext, llvm::SmallVector<CallCleanUp> &callCleanUps) {1635 using PassBy = Fortran::lower::CallerInterface::PassEntityBy;1636 mlir::Location loc = callContext.loc;1637 bool mustRemapActualToDummyDescriptors = false;1638 fir::FirOpBuilder &builder = callContext.getBuilder();1639 for (auto [preparedActual, arg] :1640 llvm::zip(loweredActuals, caller.getPassedArguments())) {1641 mlir::Type argTy = callSiteType.getInput(arg.firArgument);1642 if (!preparedActual) {1643 // Optional dummy argument for which there is no actual argument.1644 caller.placeInput(arg, builder.genAbsentOp(loc, argTy));1645 continue;1646 }1647 1648 switch (arg.passBy) {1649 case PassBy::Value: {1650 // True pass-by-value semantics.1651 assert(!preparedActual->handleDynamicOptional() && "cannot be optional");1652 hlfir::Entity actual = preparedActual->getActual(loc, builder);1653 hlfir::Entity value = hlfir::loadTrivialScalar(loc, builder, actual);1654 1655 mlir::Type eleTy = value.getFortranElementType();1656 if (fir::isa_builtin_cptr_type(eleTy)) {1657 // Pass-by-value argument of type(C_PTR/C_FUNPTR).1658 // Load the __address component and pass it by value.1659 if (value.isValue()) {1660 auto associate = hlfir::genAssociateExpr(loc, builder, value, eleTy,1661 "adapt.cptrbyval");1662 value = hlfir::Entity{genRecordCPtrValueArg(1663 builder, loc, associate.getFirBase(), eleTy)};1664 hlfir::EndAssociateOp::create(builder, loc, associate);1665 } else {1666 value =1667 hlfir::Entity{genRecordCPtrValueArg(builder, loc, value, eleTy)};1668 }1669 } else if (fir::isa_derived(value.getFortranElementType()) ||1670 value.isCharacter()) {1671 // BIND(C), VALUE derived type or character. The value must really1672 // be loaded here.1673 auto [exv, cleanup] = hlfir::convertToValue(loc, builder, value);1674 mlir::Value loadedValue = fir::getBase(exv);1675 // Character actual arguments may have unknown length or a length longer1676 // than one. Cast the memory ref to the dummy type so that the load is1677 // valid and only loads what is needed.1678 if (mlir::Type baseTy = fir::dyn_cast_ptrEleTy(loadedValue.getType()))1679 if (fir::isa_char(baseTy))1680 loadedValue = builder.createConvert(1681 loc, fir::ReferenceType::get(argTy), loadedValue);1682 if (fir::isa_ref_type(loadedValue.getType()))1683 loadedValue = fir::LoadOp::create(builder, loc, loadedValue);1684 caller.placeInput(arg, loadedValue);1685 if (cleanup)1686 (*cleanup)();1687 break;1688 }1689 // For %VAL arguments, we should pass the value directly without1690 // conversion to reference types.1691 caller.placeInput(arg, builder.createConvert(loc, argTy, value));1692 1693 } break;1694 case PassBy::BaseAddressValueAttribute:1695 case PassBy::CharBoxValueAttribute:1696 case PassBy::Box:1697 case PassBy::BaseAddress:1698 case PassBy::BoxChar: {1699 PreparedDummyArgument preparedDummy = prepareUserCallActualArgument(1700 loc, builder, *preparedActual, argTy, arg, callContext);1701 callCleanUps.append(preparedDummy.cleanups.rbegin(),1702 preparedDummy.cleanups.rend());1703 caller.placeInput(arg, preparedDummy.dummy);1704 if (arg.passBy == PassBy::Box)1705 mustRemapActualToDummyDescriptors |=1706 arg.isSequenceAssociatedDescriptor();1707 } break;1708 case PassBy::BoxProcRef: {1709 PreparedDummyArgument preparedDummy =1710 prepareProcedurePointerActualArgument(loc, builder, *preparedActual,1711 argTy, arg, callContext);1712 callCleanUps.append(preparedDummy.cleanups.rbegin(),1713 preparedDummy.cleanups.rend());1714 caller.placeInput(arg, preparedDummy.dummy);1715 } break;1716 case PassBy::AddressAndLength:1717 // PassBy::AddressAndLength is only used for character results. Results1718 // are not handled here.1719 fir::emitFatalError(1720 loc, "unexpected PassBy::AddressAndLength for actual arguments");1721 break;1722 case PassBy::CharProcTuple: {1723 hlfir::Entity actual = preparedActual->getActual(loc, builder);1724 if (actual.isProcedurePointer())1725 actual = hlfir::derefPointersAndAllocatables(loc, builder, actual);1726 if (!fir::isCharacterProcedureTuple(actual.getType()))1727 actual = fixProcedureDummyMismatch(loc, builder, actual, argTy);1728 caller.placeInput(arg, actual);1729 } break;1730 case PassBy::MutableBox: {1731 const Fortran::lower::SomeExpr *expr = arg.entity->UnwrapExpr();1732 // C709 and C710.1733 assert(expr && "cannot pass TYPE(*) to POINTER or ALLOCATABLE");1734 hlfir::Entity actual = preparedActual->getActual(loc, builder);1735 if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(1736 *expr)) {1737 // If expr is NULL(), the mutableBox created must be a deallocated1738 // pointer with the dummy argument characteristics (see table 16.51739 // in Fortran 2018 standard).1740 // No length parameters are set for the created box because any non1741 // deferred type parameters of the dummy will be evaluated on the1742 // callee side, and it is illegal to use NULL without a MOLD if any1743 // dummy length parameters are assumed.1744 mlir::Type boxTy = fir::dyn_cast_ptrEleTy(argTy);1745 assert(boxTy && mlir::isa<fir::BaseBoxType>(boxTy) &&1746 "must be a fir.box type");1747 mlir::Value boxStorage =1748 fir::factory::genNullBoxStorage(builder, loc, boxTy);1749 caller.placeInput(arg, boxStorage);1750 continue;1751 }1752 if (fir::isPointerType(argTy) &&1753 !Fortran::evaluate::IsObjectPointer(*expr)) {1754 // Passing a non POINTER actual argument to a POINTER dummy argument.1755 // Create a pointer of the dummy argument type and assign the actual1756 // argument to it.1757 auto dataTy = llvm::cast<fir::BaseBoxType>(fir::unwrapRefType(argTy));1758 fir::ExtendedValue actualExv = Fortran::lower::convertToAddress(1759 loc, callContext.converter, actual, callContext.stmtCtx,1760 hlfir::getFortranElementType(dataTy));1761 // If the dummy is an assumed-rank pointer, allocate a pointer1762 // descriptor with the actual argument rank (if it is not assumed-rank1763 // itself).1764 if (dataTy.isAssumedRank()) {1765 dataTy =1766 dataTy.getBoxTypeWithNewShape(fir::getBase(actualExv).getType());1767 }1768 mlir::Value irBox = builder.createTemporary(loc, dataTy);1769 fir::MutableBoxValue ptrBox(irBox,1770 /*nonDeferredParams=*/mlir::ValueRange{},1771 /*mutableProperties=*/{});1772 fir::factory::associateMutableBox(builder, loc, ptrBox, actualExv,1773 /*lbounds=*/{});1774 caller.placeInput(arg, irBox);1775 continue;1776 }1777 // Passing a POINTER to a POINTER, or an ALLOCATABLE to an ALLOCATABLE.1778 assert(actual.isMutableBox() && "actual must be a mutable box");1779 if (fir::isAllocatableType(argTy) && arg.isIntentOut() &&1780 callContext.isBindcCall()) {1781 // INTENT(OUT) allocatables are deallocated on the callee side,1782 // but BIND(C) procedures may be implemented in C, so deallocation is1783 // also done on the caller side (if the procedure is implemented in1784 // Fortran, the deallocation attempt in the callee will be a no-op).1785 auto [exv, cleanup] =1786 hlfir::translateToExtendedValue(loc, builder, actual);1787 const auto *mutableBox = exv.getBoxOf<fir::MutableBoxValue>();1788 assert(mutableBox && !cleanup && "expect allocatable");1789 Fortran::lower::genDeallocateIfAllocated(callContext.converter,1790 *mutableBox, loc);1791 }1792 caller.placeInput(arg, actual);1793 } break;1794 }1795 }1796 1797 // Handle cases where caller must allocate the result or a fir.box for it.1798 if (mustRemapActualToDummyDescriptors)1799 remapActualToDummyDescriptors(loc, callContext.converter,1800 callContext.symMap, loweredActuals, caller,1801 callContext.isBindcCall());1802}1803 1804/// Lower calls to user procedures with actual arguments that have been1805/// pre-lowered but not yet prepared according to the interface.1806/// This can be called for elemental procedures, but only with scalar1807/// arguments: if there are array arguments, it must be provided with1808/// the array argument elements value and will return the corresponding1809/// scalar result value.1810static std::optional<hlfir::EntityWithAttributes>1811genUserCall(Fortran::lower::PreparedActualArguments &loweredActuals,1812 Fortran::lower::CallerInterface &caller,1813 mlir::FunctionType callSiteType, CallContext &callContext) {1814 mlir::Location loc = callContext.loc;1815 llvm::SmallVector<CallCleanUp> callCleanUps;1816 fir::FirOpBuilder &builder = callContext.getBuilder();1817 1818 prepareUserCallArguments(loweredActuals, caller, callSiteType, callContext,1819 callCleanUps);1820 1821 // Prepare lowered arguments according to the interface1822 // and map the lowered values to the dummy1823 // arguments.1824 auto [loweredResult, resultIsFinalized] = Fortran::lower::genCallOpAndResult(1825 loc, callContext.converter, callContext.symMap, callContext.stmtCtx,1826 caller, callSiteType, callContext.resultType,1827 callContext.isElementalProcWithArrayArgs());1828 1829 // Clean-up associations and copy-in.1830 // The association clean-ups are postponed to the end of the statement1831 // lowering. The copy-in clean-ups may be delayed as well,1832 // but they are done immediately after the call currently.1833 llvm::SmallVector<CallCleanUp> associateCleanups;1834 for (auto cleanUp : callCleanUps) {1835 auto postponed =1836 cleanUp.genCleanUp(loc, builder, /*postponeAssociates=*/true);1837 if (postponed)1838 associateCleanups.push_back(*postponed);1839 }1840 1841 fir::FirOpBuilder *bldr = &builder;1842 callContext.stmtCtx.attachCleanup([=]() {1843 for (auto cleanUp : associateCleanups)1844 (void)cleanUp.genCleanUp(loc, *bldr, /*postponeAssociates=*/false);1845 });1846 if (auto *entity = std::get_if<hlfir::EntityWithAttributes>(&loweredResult))1847 return *entity;1848 1849 auto &result = std::get<fir::ExtendedValue>(loweredResult);1850 1851 // For procedure pointer function result, just return the call.1852 if (callContext.resultType &&1853 mlir::isa<fir::BoxProcType>(*callContext.resultType))1854 return hlfir::EntityWithAttributes(fir::getBase(result));1855 1856 if (!fir::getBase(result))1857 return std::nullopt; // subroutine call.1858 1859 if (fir::isPointerType(fir::getBase(result).getType()))1860 return extendedValueToHlfirEntity(loc, builder, result, tempResultName);1861 1862 if (!resultIsFinalized) {1863 hlfir::Entity resultEntity =1864 extendedValueToHlfirEntity(loc, builder, result, tempResultName);1865 resultEntity = loadTrivialScalar(loc, builder, resultEntity);1866 if (resultEntity.isVariable()) {1867 // If the result has no finalization, it can be moved into an expression.1868 // In such case, the expression should not be freed after its use since1869 // the result is stack allocated or deallocation (for allocatable results)1870 // was already inserted in genCallOpAndResult.1871 auto asExpr =1872 hlfir::AsExprOp::create(builder, loc, resultEntity,1873 /*mustFree=*/builder.createBool(loc, false));1874 return hlfir::EntityWithAttributes{asExpr.getResult()};1875 }1876 return hlfir::EntityWithAttributes{resultEntity};1877 }1878 // If the result has finalization, it cannot be moved because use of its1879 // value have been created in the statement context and may be emitted1880 // after the hlfir.expr destroy, so the result is kept as a variable in1881 // HLFIR. This may lead to copies when passing the result to an argument1882 // with VALUE, and this do not convey the fact that the result will not1883 // change, but is correct, and using hlfir.expr without the move would1884 // trigger a copy that may be avoided.1885 1886 // Load allocatable results before emitting the hlfir.declare and drop its1887 // lower bounds: this is not a variable From the Fortran point of view, so1888 // the lower bounds are ones when inquired on the caller side.1889 const auto *allocatable = result.getBoxOf<fir::MutableBoxValue>();1890 fir::ExtendedValue loadedResult =1891 allocatable1892 ? fir::factory::genMutableBoxRead(builder, loc, *allocatable,1893 /*mayBePolymorphic=*/true,1894 /*preserveLowerBounds=*/false)1895 : result;1896 return extendedValueToHlfirEntity(loc, builder, loadedResult, tempResultName);1897}1898 1899/// Create an optional dummy argument value from an entity that may be1900/// absent. \p actualGetter callback returns hlfir::Entity denoting1901/// the lowered actual argument. \p actualGetter can only return numerical1902/// or logical scalar entity.1903/// If the entity is considered absent according to 15.5.2.12 point 1., the1904/// returned value is zero (or false), otherwise it is the value of the entity.1905/// \p eleType specifies the entity's Fortran element type.1906template <typename T>1907static ExvAndCleanup genOptionalValue(fir::FirOpBuilder &builder,1908 mlir::Location loc, mlir::Type eleType,1909 T actualGetter, mlir::Value isPresent) {1910 return {builder1911 .genIfOp(loc, {eleType}, isPresent,1912 /*withElseRegion=*/true)1913 .genThen([&]() {1914 hlfir::Entity entity = actualGetter(loc, builder);1915 assert(eleType == entity.getFortranElementType() &&1916 "result type mismatch in genOptionalValue");1917 assert(entity.isScalar() && fir::isa_trivial(eleType) &&1918 "must be a numerical or logical scalar");1919 mlir::Value val =1920 hlfir::loadTrivialScalar(loc, builder, entity);1921 fir::ResultOp::create(builder, loc, val);1922 })1923 .genElse([&]() {1924 mlir::Value zero =1925 fir::factory::createZeroValue(builder, loc, eleType);1926 fir::ResultOp::create(builder, loc, zero);1927 })1928 .getResults()[0],1929 std::nullopt};1930}1931 1932/// Create an optional dummy argument address from \p entity that may be1933/// absent. If \p entity is considered absent according to 15.5.2.12 point 1.,1934/// the returned value is a null pointer, otherwise it is the address of \p1935/// entity.1936static ExvAndCleanup genOptionalAddr(fir::FirOpBuilder &builder,1937 mlir::Location loc, hlfir::Entity entity,1938 mlir::Value isPresent) {1939 auto [exv, cleanup] = hlfir::translateToExtendedValue(loc, builder, entity);1940 // If it is an exv pointer/allocatable, then it cannot be absent1941 // because it is passed to a non-pointer/non-allocatable.1942 if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>())1943 return {fir::factory::genMutableBoxRead(builder, loc, *box), cleanup};1944 // If this is not a POINTER or ALLOCATABLE, then it is already an OPTIONAL1945 // address and can be passed directly.1946 return {exv, cleanup};1947}1948 1949/// Create an optional dummy argument address from \p entity that may be1950/// absent. If \p entity is considered absent according to 15.5.2.12 point 1.,1951/// the returned value is an absent fir.box, otherwise it is a fir.box1952/// describing \p entity.1953static ExvAndCleanup genOptionalBox(fir::FirOpBuilder &builder,1954 mlir::Location loc, hlfir::Entity entity,1955 mlir::Value isPresent) {1956 auto [exv, cleanup] = hlfir::translateToExtendedValue(loc, builder, entity);1957 1958 // Non allocatable/pointer optional box -> simply forward1959 if (exv.getBoxOf<fir::BoxValue>())1960 return {exv, cleanup};1961 1962 fir::ExtendedValue newExv = exv;1963 // Optional allocatable/pointer -> Cannot be absent, but need to translate1964 // unallocated/diassociated into absent fir.box.1965 if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>())1966 newExv = fir::factory::genMutableBoxRead(builder, loc, *box);1967 1968 // createBox will not do create any invalid memory dereferences if exv is1969 // absent. The created fir.box will not be usable, but the SelectOp below1970 // ensures it won't be.1971 mlir::Value box = builder.createBox(loc, newExv);1972 mlir::Type boxType = box.getType();1973 auto absent = fir::AbsentOp::create(builder, loc, boxType);1974 auto boxOrAbsent = mlir::arith::SelectOp::create(builder, loc, boxType,1975 isPresent, box, absent);1976 return {fir::BoxValue(boxOrAbsent), cleanup};1977}1978 1979/// Lower calls to intrinsic procedures with custom optional handling where the1980/// actual arguments have been pre-lowered1981static std::optional<hlfir::EntityWithAttributes> genCustomIntrinsicRefCore(1982 Fortran::lower::PreparedActualArguments &loweredActuals,1983 const Fortran::evaluate::SpecificIntrinsic *intrinsic,1984 CallContext &callContext) {1985 auto &builder = callContext.getBuilder();1986 const auto &loc = callContext.loc;1987 assert(intrinsic &&1988 Fortran::lower::intrinsicRequiresCustomOptionalHandling(1989 callContext.procRef, *intrinsic, callContext.converter));1990 1991 // helper to get a particular prepared argument1992 auto getArgument = [&](std::size_t i, bool loadArg) -> fir::ExtendedValue {1993 if (!loweredActuals[i])1994 return fir::getAbsentIntrinsicArgument();1995 hlfir::Entity actual = loweredActuals[i]->getActual(loc, builder);1996 if (loadArg && fir::conformsWithPassByRef(actual.getType())) {1997 return hlfir::loadTrivialScalar(loc, builder, actual);1998 }1999 return Fortran::lower::translateToExtendedValue(loc, builder, actual,2000 callContext.stmtCtx);2001 };2002 // helper to get the isPresent flag for a particular prepared argument2003 auto isPresent = [&](std::size_t i) -> std::optional<mlir::Value> {2004 if (!loweredActuals[i])2005 return {builder.createBool(loc, false)};2006 if (loweredActuals[i]->handleDynamicOptional())2007 return {loweredActuals[i]->getIsPresent()};2008 return std::nullopt;2009 };2010 2011 assert(callContext.resultType &&2012 "the elemental intrinsics with custom handling are all functions");2013 // if callContext.resultType is an array then this was originally an elemental2014 // call. What we are lowering here is inside the kernel of the hlfir.elemental2015 // so we should return the scalar type. If the return type is already a scalar2016 // then it should be unchanged here.2017 mlir::Type resTy = hlfir::getFortranElementType(*callContext.resultType);2018 fir::ExtendedValue result = Fortran::lower::lowerCustomIntrinsic(2019 builder, loc, callContext.getProcedureName(), resTy, isPresent,2020 getArgument, loweredActuals.size(), callContext.stmtCtx);2021 2022 return {hlfir::EntityWithAttributes{extendedValueToHlfirEntity(2023 loc, builder, result, ".tmp.custom_intrinsic_result")}};2024}2025 2026/// Lower calls to intrinsic procedures with actual arguments that have been2027/// pre-lowered but have not yet been prepared according to the interface.2028static std::optional<hlfir::EntityWithAttributes>2029genIntrinsicRefCore(Fortran::lower::PreparedActualArguments &loweredActuals,2030 const Fortran::evaluate::SpecificIntrinsic *intrinsic,2031 const fir::IntrinsicHandlerEntry &intrinsicEntry,2032 CallContext &callContext) {2033 auto &converter = callContext.converter;2034 if (intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling(2035 callContext.procRef, *intrinsic, converter))2036 return genCustomIntrinsicRefCore(loweredActuals, intrinsic, callContext);2037 llvm::SmallVector<fir::ExtendedValue> operands;2038 llvm::SmallVector<hlfir::CleanupFunction> cleanupFns;2039 auto addToCleanups = [&cleanupFns](std::optional<hlfir::CleanupFunction> fn) {2040 if (fn)2041 cleanupFns.emplace_back(std::move(*fn));2042 };2043 auto &stmtCtx = callContext.stmtCtx;2044 fir::FirOpBuilder &builder = callContext.getBuilder();2045 mlir::Location loc = callContext.loc;2046 const fir::IntrinsicArgumentLoweringRules *argLowering =2047 intrinsicEntry.getArgumentLoweringRules();2048 for (auto arg : llvm::enumerate(loweredActuals)) {2049 if (!arg.value()) {2050 operands.emplace_back(fir::getAbsentIntrinsicArgument());2051 continue;2052 }2053 if (!argLowering) {2054 // No argument lowering instruction, lower by value.2055 assert(!arg.value()->handleDynamicOptional() &&2056 "should use genOptionalValue");2057 hlfir::Entity actual = arg.value()->getActual(loc, builder);2058 operands.emplace_back(2059 Fortran::lower::convertToValue(loc, converter, actual, stmtCtx));2060 continue;2061 }2062 // Helper to get the type of the Fortran expression in case it is a2063 // computed value that must be placed in memory (logicals are computed as2064 // i1, but must be placed in memory as fir.logical).2065 auto getActualFortranElementType = [&]() -> mlir::Type {2066 if (const Fortran::lower::SomeExpr *expr =2067 callContext.procRef.UnwrapArgExpr(arg.index())) {2068 2069 mlir::Type type = converter.genType(*expr);2070 return hlfir::getFortranElementType(type);2071 }2072 // TYPE(*): is already in memory anyway. Can return none2073 // here.2074 return builder.getNoneType();2075 };2076 // Ad-hoc argument lowering handling.2077 fir::ArgLoweringRule argRules =2078 fir::lowerIntrinsicArgumentAs(*argLowering, arg.index());2079 if (arg.value()->handleDynamicOptional()) {2080 mlir::Value isPresent = arg.value()->getIsPresent();2081 switch (argRules.lowerAs) {2082 case fir::LowerIntrinsicArgAs::Value: {2083 // In case of elemental call, getActual() may produce2084 // a designator denoting the array element to be passed2085 // to the subprogram. If the actual array is dynamically2086 // optional the designator must be generated under2087 // isPresent check, because the box bounds reads will be2088 // generated in the codegen. These reads are illegal,2089 // if the dynamically optional argument is absent.2090 auto getActualCb = [&](mlir::Location loc,2091 fir::FirOpBuilder &builder) -> hlfir::Entity {2092 return arg.value()->getActual(loc, builder);2093 };2094 auto [exv, cleanup] =2095 genOptionalValue(builder, loc, getActualFortranElementType(),2096 getActualCb, isPresent);2097 addToCleanups(std::move(cleanup));2098 operands.emplace_back(exv);2099 continue;2100 }2101 case fir::LowerIntrinsicArgAs::Addr: {2102 hlfir::Entity actual = arg.value()->getActual(loc, builder);2103 auto [exv, cleanup] = genOptionalAddr(builder, loc, actual, isPresent);2104 addToCleanups(std::move(cleanup));2105 operands.emplace_back(exv);2106 continue;2107 }2108 case fir::LowerIntrinsicArgAs::Box: {2109 hlfir::Entity actual = arg.value()->getActual(loc, builder);2110 auto [exv, cleanup] = genOptionalBox(builder, loc, actual, isPresent);2111 addToCleanups(std::move(cleanup));2112 operands.emplace_back(exv);2113 continue;2114 }2115 case fir::LowerIntrinsicArgAs::Inquired: {2116 hlfir::Entity actual = arg.value()->getActual(loc, builder);2117 auto [exv, cleanup] =2118 hlfir::translateToExtendedValue(loc, builder, actual);2119 addToCleanups(std::move(cleanup));2120 operands.emplace_back(exv);2121 continue;2122 }2123 }2124 llvm_unreachable("bad switch");2125 }2126 2127 hlfir::Entity actual = arg.value()->getActual(loc, builder);2128 switch (argRules.lowerAs) {2129 case fir::LowerIntrinsicArgAs::Value:2130 operands.emplace_back(2131 Fortran::lower::convertToValue(loc, converter, actual, stmtCtx));2132 continue;2133 case fir::LowerIntrinsicArgAs::Addr:2134 operands.emplace_back(Fortran::lower::convertToAddress(2135 loc, converter, actual, stmtCtx, getActualFortranElementType()));2136 continue;2137 case fir::LowerIntrinsicArgAs::Box:2138 operands.emplace_back(Fortran::lower::convertToBox(2139 loc, converter, actual, stmtCtx, getActualFortranElementType()));2140 continue;2141 case fir::LowerIntrinsicArgAs::Inquired:2142 if (const Fortran::lower::SomeExpr *expr =2143 callContext.procRef.UnwrapArgExpr(arg.index())) {2144 if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(2145 *expr)) {2146 // NULL() pointer without a MOLD must be passed as a deallocated2147 // pointer (see table 16.5 in Fortran 2018 standard).2148 // !fir.box<!fir.ptr<none>> should always be valid in this context.2149 mlir::Type noneTy = mlir::NoneType::get(builder.getContext());2150 mlir::Type nullPtrTy = fir::PointerType::get(noneTy);2151 mlir::Type boxTy = fir::BoxType::get(nullPtrTy);2152 mlir::Value boxStorage =2153 fir::factory::genNullBoxStorage(builder, loc, boxTy);2154 hlfir::EntityWithAttributes nullBoxEntity =2155 extendedValueToHlfirEntity(loc, builder, boxStorage,2156 ".tmp.null_box");2157 operands.emplace_back(Fortran::lower::translateToExtendedValue(2158 loc, builder, nullBoxEntity, stmtCtx));2159 continue;2160 }2161 }2162 // Place hlfir.expr in memory, and unbox fir.boxchar. Other entities2163 // are translated to fir::ExtendedValue without transformation (notably,2164 // pointers/allocatable are not dereferenced).2165 // TODO: once lowering to FIR retires, UBOUND and LBOUND can be simplified2166 // since the fir.box lowered here are now guaranteed to contain the local2167 // lower bounds thanks to the hlfir.declare (the extra rebox can be2168 // removed).2169 operands.emplace_back(Fortran::lower::translateToExtendedValue(2170 loc, builder, actual, stmtCtx));2171 continue;2172 }2173 llvm_unreachable("bad switch");2174 }2175 // genIntrinsicCall needs the scalar type, even if this is a transformational2176 // procedure returning an array.2177 std::optional<mlir::Type> scalarResultType;2178 if (callContext.resultType)2179 scalarResultType = hlfir::getFortranElementType(*callContext.resultType);2180 const std::string intrinsicName = callContext.getProcedureName();2181 // Let the intrinsic library lower the intrinsic procedure call.2182 auto [resultExv, mustBeFreed] = genIntrinsicCall(2183 builder, loc, intrinsicEntry, scalarResultType, operands, &converter);2184 for (const hlfir::CleanupFunction &fn : cleanupFns)2185 fn();2186 if (!fir::getBase(resultExv))2187 return std::nullopt;2188 hlfir::EntityWithAttributes resultEntity = extendedValueToHlfirEntity(2189 loc, builder, resultExv, ".tmp.intrinsic_result");2190 // Move result into memory into an hlfir.expr since they are immutable from2191 // that point, and the result storage is some temp. "Null" is special: it2192 // returns a null pointer variable that should not be transformed into a value2193 // (what matters is the memory address).2194 if (resultEntity.isVariable() && intrinsicName != "null") {2195 assert(!fir::isa_trivial(fir::unwrapRefType(resultEntity.getType())) &&2196 "expect intrinsic scalar results to not be in memory");2197 hlfir::AsExprOp asExpr;2198 // Character/Derived MERGE lowering returns one of its argument address2199 // (this is the only intrinsic implemented in that way so far). The2200 // ownership of this address cannot be taken here since it may not be a2201 // temp.2202 if (intrinsicName == "merge")2203 asExpr = hlfir::AsExprOp::create(builder, loc, resultEntity);2204 else2205 asExpr = hlfir::AsExprOp::create(builder, loc, resultEntity,2206 builder.createBool(loc, mustBeFreed));2207 resultEntity = hlfir::EntityWithAttributes{asExpr.getResult()};2208 }2209 return resultEntity;2210}2211 2212/// Lower calls to intrinsic procedures with actual arguments that have been2213/// pre-lowered but have not yet been prepared according to the interface.2214static std::optional<hlfir::EntityWithAttributes> genHLFIRIntrinsicRefCore(2215 Fortran::lower::PreparedActualArguments &loweredActuals,2216 const Fortran::evaluate::SpecificIntrinsic *intrinsic,2217 const fir::IntrinsicHandlerEntry &intrinsicEntry,2218 CallContext &callContext) {2219 // Try lowering transformational intrinsic ops to HLFIR ops if enabled2220 // (transformational always have a result type)2221 if (useHlfirIntrinsicOps && callContext.resultType) {2222 fir::FirOpBuilder &builder = callContext.getBuilder();2223 mlir::Location loc = callContext.loc;2224 const std::string intrinsicName = callContext.getProcedureName();2225 const fir::IntrinsicArgumentLoweringRules *argLowering =2226 intrinsicEntry.getArgumentLoweringRules();2227 mlir::Type resultType =2228 callContext.isElementalProcWithArrayArgs()2229 ? hlfir::getFortranElementType(*callContext.resultType)2230 : *callContext.resultType;2231 2232 std::optional<hlfir::EntityWithAttributes> res =2233 Fortran::lower::lowerHlfirIntrinsic(builder, loc, intrinsicName,2234 loweredActuals, argLowering,2235 resultType);2236 if (res)2237 return res;2238 }2239 2240 // fallback to calling the intrinsic via fir.call2241 return genIntrinsicRefCore(loweredActuals, intrinsic, intrinsicEntry,2242 callContext);2243}2244 2245namespace {2246template <typename ElementalCallBuilderImpl>2247class ElementalCallBuilder {2248public:2249 std::optional<hlfir::EntityWithAttributes>2250 genElementalCall(Fortran::lower::PreparedActualArguments &loweredActuals,2251 bool isImpure, CallContext &callContext) {2252 mlir::Location loc = callContext.loc;2253 fir::FirOpBuilder &builder = callContext.getBuilder();2254 unsigned numArgs = loweredActuals.size();2255 // Step 1: dereference pointers/allocatables and compute elemental shape.2256 mlir::Value shape;2257 Fortran::lower::PreparedActualArgument *optionalWithShape;2258 // 10.1.4 p5. Impure elemental procedures must be called in element order.2259 bool mustBeOrdered = isImpure;2260 for (unsigned i = 0; i < numArgs; ++i) {2261 auto &preparedActual = loweredActuals[i];2262 if (preparedActual) {2263 // Elemental procedure dummy arguments cannot be pointer/allocatables2264 // (C15100), so it is safe to dereference any pointer or allocatable2265 // actual argument now instead of doing this inside the elemental2266 // region.2267 preparedActual->derefPointersAndAllocatables(loc, builder);2268 // Better to load scalars outside of the loop when possible.2269 if (!preparedActual->handleDynamicOptional() &&2270 impl().canLoadActualArgumentBeforeLoop(i))2271 preparedActual->loadTrivialScalar(loc, builder);2272 // TODO: merge shape instead of using the first one.2273 if (!shape && preparedActual->isArray()) {2274 if (preparedActual->handleDynamicOptional())2275 optionalWithShape = &*preparedActual;2276 else2277 shape = preparedActual->genShape(loc, builder);2278 }2279 // 15.8.3 p1. Elemental procedure with intent(out)/intent(inout)2280 // arguments must be called in element order.2281 if (impl().argMayBeModifiedByCall(i))2282 mustBeOrdered = true;2283 }2284 }2285 if (!shape && optionalWithShape) {2286 // If all array operands appear in optional positions, then none of them2287 // is allowed to be absent as per 15.5.2.12 point 3. (6). Just pick the2288 // first operand.2289 shape = optionalWithShape->genShape(loc, builder);2290 // TODO: There is an opportunity to add a runtime check here that2291 // this array is present as required. Also, the optionality of all actual2292 // could be checked and reset given the Fortran requirement.2293 optionalWithShape->resetOptionalAspect();2294 }2295 assert(shape &&2296 "elemental array calls must have at least one array arguments");2297 2298 // Evaluate the actual argument array expressions before the elemental2299 // call of an impure subprogram or a subprogram with intent(out) or2300 // intent(inout) arguments. Note that the scalar arguments are handled2301 // above.2302 if (mustBeOrdered) {2303 for (auto &preparedActual : loweredActuals) {2304 if (preparedActual) {2305 if (hlfir::AssociateOp associate =2306 preparedActual->associateIfArrayExpr(loc, builder)) {2307 fir::FirOpBuilder *bldr = &builder;2308 callContext.stmtCtx.attachCleanup([=]() {2309 hlfir::EndAssociateOp::create(*bldr, loc, associate);2310 });2311 }2312 }2313 }2314 }2315 2316 // Push a new local scope so that any temps made inside the elemental2317 // iterations are cleaned up inside the iterations.2318 if (!callContext.resultType) {2319 // Subroutine case. Generate call inside loop nest.2320 hlfir::LoopNest loopNest =2321 hlfir::genLoopNest(loc, builder, shape, !mustBeOrdered);2322 mlir::ValueRange oneBasedIndices = loopNest.oneBasedIndices;2323 auto insPt = builder.saveInsertionPoint();2324 builder.setInsertionPointToStart(loopNest.body);2325 callContext.stmtCtx.pushScope();2326 for (auto &preparedActual : loweredActuals)2327 if (preparedActual)2328 preparedActual->setElementalIndices(oneBasedIndices);2329 impl().genElementalKernel(loweredActuals, callContext);2330 callContext.stmtCtx.finalizeAndPop();2331 builder.restoreInsertionPoint(insPt);2332 return std::nullopt;2333 }2334 // Function case: generate call inside hlfir.elemental2335 mlir::Type elementType =2336 hlfir::getFortranElementType(*callContext.resultType);2337 // Get result length parameters.2338 llvm::SmallVector<mlir::Value> typeParams;2339 if (mlir::isa<fir::CharacterType>(elementType) ||2340 fir::isRecordWithTypeParameters(elementType)) {2341 auto charType = mlir::dyn_cast<fir::CharacterType>(elementType);2342 if (charType && charType.hasConstantLen())2343 typeParams.push_back(builder.createIntegerConstant(2344 loc, builder.getIndexType(), charType.getLen()));2345 else if (charType)2346 typeParams.push_back(impl().computeDynamicCharacterResultLength(2347 loweredActuals, callContext));2348 else2349 TODO(2350 loc,2351 "compute elemental PDT function result length parameters in HLFIR");2352 }2353 auto genKernel = [&](mlir::Location l, fir::FirOpBuilder &b,2354 mlir::ValueRange oneBasedIndices) -> hlfir::Entity {2355 callContext.stmtCtx.pushScope();2356 for (auto &preparedActual : loweredActuals)2357 if (preparedActual)2358 preparedActual->setElementalIndices(oneBasedIndices);2359 auto res = *impl().genElementalKernel(loweredActuals, callContext);2360 callContext.stmtCtx.finalizeAndPop();2361 // Note that an hlfir.destroy is not emitted for the result since it2362 // is still used by the hlfir.yield_element that also marks its last2363 // use.2364 return res;2365 };2366 mlir::Value polymorphicMold;2367 if (fir::isPolymorphicType(*callContext.resultType))2368 polymorphicMold =2369 impl().getPolymorphicResultMold(loweredActuals, callContext);2370 mlir::Value elemental =2371 hlfir::genElementalOp(loc, builder, elementType, shape, typeParams,2372 genKernel, !mustBeOrdered, polymorphicMold);2373 // If the function result requires finalization, then it has to be done2374 // for the array result of the elemental call. We have to communicate2375 // this via the DestroyOp's attribute.2376 bool mustFinalizeExpr = impl().resultMayRequireFinalization(callContext);2377 fir::FirOpBuilder *bldr = &builder;2378 callContext.stmtCtx.attachCleanup([=]() {2379 hlfir::DestroyOp::create(*bldr, loc, elemental, mustFinalizeExpr);2380 });2381 return hlfir::EntityWithAttributes{elemental};2382 }2383 2384private:2385 ElementalCallBuilderImpl &impl() {2386 return *static_cast<ElementalCallBuilderImpl *>(this);2387 }2388};2389 2390/// Helper for computing elemental function result specification2391/// expressions that depends on dummy symbols. See2392/// computeDynamicCharacterResultLength below.2393static mlir::Value genMockDummyForElementalResultSpecifications(2394 fir::FirOpBuilder &builder, mlir::Location loc, mlir::Type dummyType,2395 Fortran::lower::PreparedActualArgument &preparedActual) {2396 // One is used as the mock address instead of NULL so that PRESENT inquires2397 // work (this is the only valid thing that specification can do with the2398 // address thanks to Fortran 2023 C15121).2399 mlir::Value one =2400 builder.createIntegerConstant(loc, builder.getIntPtrType(), 1);2401 if (auto boxCharType = llvm::dyn_cast<fir::BoxCharType>(dummyType)) {2402 mlir::Value addr = builder.createConvert(2403 loc, fir::ReferenceType::get(boxCharType.getEleTy()), one);2404 mlir::Value len = preparedActual.genCharLength(loc, builder);2405 return fir::EmboxCharOp::create(builder, loc, boxCharType, addr, len);2406 }2407 if (auto box = llvm::dyn_cast<fir::BaseBoxType>(dummyType)) {2408 mlir::Value addr =2409 builder.createConvert(loc, box.getBaseAddressType(), one);2410 llvm::SmallVector<mlir::Value> lenParams;2411 preparedActual.genLengthParameters(loc, builder, lenParams);2412 mlir::Value mold;2413 if (fir::isPolymorphicType(box))2414 mold = preparedActual.getPolymorphicMold(loc);2415 return fir::EmboxOp::create(builder, loc, box, addr,2416 /*shape=*/mlir::Value{},2417 /*slice=*/mlir::Value{}, lenParams, mold);2418 }2419 // Values of arguments should not be used in elemental procedure specification2420 // expressions as per C15121, so it makes no sense to have a specification2421 // expression requiring a symbol that is passed by value (there is no good2422 // value to create here).2423 assert(fir::isa_ref_type(dummyType) &&2424 (fir::isa_trivial(fir::unwrapRefType(dummyType)) ||2425 fir::isa_char(fir::unwrapRefType(dummyType))) &&2426 "Only expect symbols inquired in elemental procedure result "2427 "specifications to be passed in memory");2428 return builder.createConvert(loc, dummyType, one);2429}2430 2431class ElementalUserCallBuilder2432 : public ElementalCallBuilder<ElementalUserCallBuilder> {2433public:2434 ElementalUserCallBuilder(Fortran::lower::CallerInterface &caller,2435 mlir::FunctionType callSiteType)2436 : caller{caller}, callSiteType{callSiteType} {}2437 std::optional<hlfir::Entity>2438 genElementalKernel(Fortran::lower::PreparedActualArguments &loweredActuals,2439 CallContext &callContext) {2440 return genUserCall(loweredActuals, caller, callSiteType, callContext);2441 }2442 2443 bool argMayBeModifiedByCall(unsigned argIdx) const {2444 assert(argIdx < caller.getPassedArguments().size() && "bad argument index");2445 return caller.getPassedArguments()[argIdx].mayBeModifiedByCall();2446 }2447 2448 bool canLoadActualArgumentBeforeLoop(unsigned argIdx) const {2449 using PassBy = Fortran::lower::CallerInterface::PassEntityBy;2450 const auto &passedArgs{caller.getPassedArguments()};2451 assert(argIdx < passedArgs.size() && "bad argument index");2452 // If the actual argument does not need to be passed via an address,2453 // or will be passed in the address of a temporary copy, it can be loaded2454 // before the elemental loop nest.2455 const auto &arg{passedArgs[argIdx]};2456 return arg.passBy == PassBy::Value ||2457 arg.passBy == PassBy::BaseAddressValueAttribute;2458 }2459 2460 mlir::Value computeDynamicCharacterResultLength(2461 Fortran::lower::PreparedActualArguments &loweredActuals,2462 CallContext &callContext) {2463 2464 fir::FirOpBuilder &builder = callContext.getBuilder();2465 mlir::Location loc = callContext.loc;2466 auto &converter = callContext.converter;2467 2468 // Gather the dummy argument symbols required directly or indirectly to2469 // evaluate the result symbol specification expressions.2470 llvm::SmallPtrSet<const Fortran::semantics::Symbol *, 4>2471 requiredDummySymbols;2472 const Fortran::semantics::Symbol &result = caller.getResultSymbol();2473 for (Fortran::lower::pft::Variable var :2474 Fortran::lower::pft::getDependentVariableList(result))2475 if (var.hasSymbol()) {2476 const Fortran::semantics::Symbol &sym = var.getSymbol();2477 if (Fortran::semantics::IsDummy(sym) && sym.owner() == result.owner())2478 requiredDummySymbols.insert(&sym);2479 }2480 2481 // Prepare mock FIR arguments for each dummy arguments required in the2482 // result specifications. These mock arguments will have the same properties2483 // (dynamic type and type parameters) as the actual arguments, except for2484 // the address. Such mock argument are needed because this evaluation is2485 // happening before the loop for the elemental call (the array result2486 // storage must be allocated before the loops if any is needed, so the2487 // result properties must be known before the loops). So it is not possible2488 // to just pick an element (like the first one) and use that because the2489 // normal argument preparation have effects (vector subscripted actual2490 // argument will require reading the vector subscript and VALUE arguments2491 // preparation involve copies of the data. This could cause segfaults in2492 // case of zero size arrays and is in general pointless extra computation2493 // since the data cannot be used in the specification expression as per2494 // C15121).2495 if (!requiredDummySymbols.empty()) {2496 const Fortran::semantics::SubprogramDetails *iface =2497 caller.getInterfaceDetails();2498 assert(iface && "interface must be explicit when result specification "2499 "depends upon dummy symbols");2500 for (auto [maybePreparedActual, arg, sym] : llvm::zip(2501 loweredActuals, caller.getPassedArguments(), iface->dummyArgs()))2502 if (requiredDummySymbols.contains(sym)) {2503 mlir::Type dummyType = callSiteType.getInput(arg.firArgument);2504 2505 if (!maybePreparedActual.has_value()) {2506 mlir::Value mockArgValue =2507 fir::AbsentOp::create(builder, loc, dummyType);2508 caller.placeInput(arg, mockArgValue);2509 continue;2510 }2511 2512 Fortran::lower::PreparedActualArgument &preparedActual =2513 maybePreparedActual.value();2514 2515 if (preparedActual.handleDynamicOptional()) {2516 mlir::Value isPresent = preparedActual.getIsPresent();2517 mlir::Value mockArgValue =2518 builder2519 .genIfOp(loc, {dummyType}, isPresent,2520 /*withElseRegion=*/true)2521 .genThen([&]() {2522 mlir::Value mockArgValue =2523 genMockDummyForElementalResultSpecifications(2524 builder, loc, dummyType, preparedActual);2525 fir::ResultOp::create(builder, loc, mockArgValue);2526 })2527 .genElse([&]() {2528 mlir::Value absent =2529 fir::AbsentOp::create(builder, loc, dummyType);2530 fir::ResultOp::create(builder, loc, absent);2531 })2532 .getResults()[0];2533 caller.placeInput(arg, mockArgValue);2534 } else {2535 mlir::Value mockArgValue =2536 genMockDummyForElementalResultSpecifications(2537 builder, loc, dummyType, preparedActual);2538 caller.placeInput(arg, mockArgValue);2539 }2540 }2541 }2542 2543 // Map symbols required by the result specification expressions to SSA2544 // values. This will both finish mapping the mock value created above if2545 // any, and deal with any module/common block variables accessed in the2546 // specification expressions.2547 // Map prepared argument to dummy symbol to be able to lower spec expr.2548 callContext.symMap.pushScope();2549 Fortran::lower::mapCallInterfaceSymbolsForResult(converter, caller,2550 callContext.symMap);2551 2552 // Evaluate the result length expression.2553 mlir::Type idxTy = builder.getIndexType();2554 auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value {2555 mlir::Value convertExpr = builder.createConvert(2556 loc, idxTy,2557 fir::getBase(converter.genExprValue(expr, callContext.stmtCtx)));2558 return fir::factory::genMaxWithZero(builder, loc, convertExpr);2559 };2560 2561 llvm::SmallVector<mlir::Value> lengths;2562 caller.walkResultLengths(2563 [&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) {2564 assert(!isAssumedSizeExtent && "result cannot be assumed-size");2565 lengths.emplace_back(lowerSpecExpr(e));2566 });2567 callContext.symMap.popScope();2568 assert(lengths.size() == 1 && "expect 1 length parameter for the result");2569 return lengths[0];2570 }2571 2572 mlir::Value getPolymorphicResultMold(2573 Fortran::lower::PreparedActualArguments &loweredActuals,2574 CallContext &callContext) {2575 fir::emitFatalError(callContext.loc,2576 "elemental function call with polymorphic result");2577 return {};2578 }2579 2580 bool resultMayRequireFinalization(CallContext &callContext) const {2581 std::optional<Fortran::evaluate::DynamicType> retTy =2582 caller.getCallDescription().proc().GetType();2583 if (!retTy)2584 return false;2585 2586 if (retTy->IsPolymorphic() || retTy->IsUnlimitedPolymorphic())2587 fir::emitFatalError(2588 callContext.loc,2589 "elemental function call with [unlimited-]polymorphic result");2590 2591 if (retTy->category() == Fortran::common::TypeCategory::Derived) {2592 const Fortran::semantics::DerivedTypeSpec &typeSpec =2593 retTy->GetDerivedTypeSpec();2594 return Fortran::semantics::IsFinalizable(typeSpec);2595 }2596 2597 return false;2598 }2599 2600private:2601 Fortran::lower::CallerInterface &caller;2602 mlir::FunctionType callSiteType;2603};2604 2605class ElementalIntrinsicCallBuilder2606 : public ElementalCallBuilder<ElementalIntrinsicCallBuilder> {2607public:2608 ElementalIntrinsicCallBuilder(2609 const Fortran::evaluate::SpecificIntrinsic *intrinsic,2610 const fir::IntrinsicHandlerEntry &intrinsicEntry, bool isFunction)2611 : intrinsic{intrinsic}, intrinsicEntry{intrinsicEntry},2612 isFunction{isFunction} {}2613 std::optional<hlfir::Entity>2614 genElementalKernel(Fortran::lower::PreparedActualArguments &loweredActuals,2615 CallContext &callContext) {2616 return genHLFIRIntrinsicRefCore(loweredActuals, intrinsic, intrinsicEntry,2617 callContext);2618 }2619 // Elemental intrinsic functions cannot modify their arguments.2620 bool argMayBeModifiedByCall(int) const { return !isFunction; }2621 bool canLoadActualArgumentBeforeLoop(int) const {2622 // Elemental intrinsic functions never need the actual addresses2623 // of their arguments.2624 return isFunction;2625 }2626 2627 mlir::Value computeDynamicCharacterResultLength(2628 Fortran::lower::PreparedActualArguments &loweredActuals,2629 CallContext &callContext) {2630 if (intrinsic)2631 if (intrinsic->name == "adjustr" || intrinsic->name == "adjustl" ||2632 intrinsic->name == "merge")2633 return loweredActuals[0].value().genCharLength(2634 callContext.loc, callContext.getBuilder());2635 // Character MIN/MAX is the min/max of the arguments length that are2636 // present.2637 TODO(callContext.loc,2638 "compute elemental character min/max function result length in HLFIR");2639 }2640 2641 mlir::Value getPolymorphicResultMold(2642 Fortran::lower::PreparedActualArguments &loweredActuals,2643 CallContext &callContext) {2644 if (!intrinsic)2645 return {};2646 2647 if (intrinsic->name == "merge") {2648 // MERGE seems to be the only elemental function that can produce2649 // polymorphic result. The MERGE's result is polymorphic iff2650 // both TSOURCE and FSOURCE are polymorphic, and they also must have2651 // the same declared and dynamic types. So any of them can be used2652 // for the mold.2653 assert(!loweredActuals.empty());2654 return loweredActuals.front()->getPolymorphicMold(callContext.loc);2655 }2656 2657 return {};2658 }2659 2660 bool resultMayRequireFinalization(2661 [[maybe_unused]] CallContext &callContext) const {2662 // FIXME: need access to the CallerInterface's return type2663 // to check if the result may need finalization (e.g. the result2664 // of MERGE).2665 return false;2666 }2667 2668private:2669 const Fortran::evaluate::SpecificIntrinsic *intrinsic;2670 fir::IntrinsicHandlerEntry intrinsicEntry;2671 const bool isFunction;2672};2673} // namespace2674 2675static std::optional<mlir::Value>2676genIsPresentIfArgMaybeAbsent(mlir::Location loc, hlfir::Entity actual,2677 const Fortran::lower::SomeExpr &expr,2678 CallContext &callContext,2679 bool passAsAllocatableOrPointer) {2680 if (!Fortran::evaluate::MayBePassedAsAbsentOptional(expr))2681 return std::nullopt;2682 fir::FirOpBuilder &builder = callContext.getBuilder();2683 if (!passAsAllocatableOrPointer &&2684 Fortran::evaluate::IsAllocatableOrPointerObject(expr)) {2685 // Passing Allocatable/Pointer to non-pointer/non-allocatable OPTIONAL.2686 // Fortran 2018 15.5.2.12 point 1: If unallocated/disassociated, it is2687 // as if the argument was absent. The main care here is to not do a2688 // copy-in/copy-out because the temp address, even though pointing to a2689 // null size storage, would not be a nullptr and therefore the argument2690 // would not be considered absent on the callee side. Note: if the2691 // allocatable/pointer is also optional, it cannot be absent as per2692 // 15.5.2.12 point 7. and 8. We rely on this to un-conditionally read2693 // the allocatable/pointer descriptor here.2694 mlir::Value addr = genVariableRawAddress(loc, builder, actual);2695 return builder.genIsNotNullAddr(loc, addr);2696 }2697 // TODO: what if passing allocatable target to optional intent(in) pointer?2698 // May fall into the category above if the allocatable is not optional.2699 2700 // Passing an optional to an optional.2701 return fir::IsPresentOp::create(builder, loc, builder.getI1Type(), actual)2702 .getResult();2703}2704 2705// Lower a reference to an elemental intrinsic procedure with array arguments2706// and custom optional handling2707static std::optional<hlfir::EntityWithAttributes>2708genCustomElementalIntrinsicRef(2709 const Fortran::evaluate::SpecificIntrinsic *intrinsic,2710 CallContext &callContext) {2711 assert(callContext.isElementalProcWithArrayArgs() &&2712 "Use genCustomIntrinsicRef for scalar calls");2713 mlir::Location loc = callContext.loc;2714 auto &converter = callContext.converter;2715 Fortran::lower::PreparedActualArguments operands;2716 assert(intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling(2717 callContext.procRef, *intrinsic, converter));2718 2719 // callback for optional arguments2720 auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) {2721 hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR(2722 loc, converter, expr, callContext.symMap, callContext.stmtCtx);2723 std::optional<mlir::Value> isPresent =2724 genIsPresentIfArgMaybeAbsent(loc, actual, expr, callContext,2725 /*passAsAllocatableOrPointer=*/false);2726 operands.emplace_back(2727 Fortran::lower::PreparedActualArgument{actual, isPresent});2728 };2729 2730 // callback for non-optional arguments2731 auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr,2732 fir::LowerIntrinsicArgAs lowerAs) {2733 hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR(2734 loc, converter, expr, callContext.symMap, callContext.stmtCtx);2735 operands.emplace_back(Fortran::lower::PreparedActualArgument{2736 actual, /*isPresent=*/std::nullopt});2737 };2738 2739 Fortran::lower::prepareCustomIntrinsicArgument(2740 callContext.procRef, *intrinsic, callContext.resultType,2741 prepareOptionalArg, prepareOtherArg, converter);2742 2743 std::optional<fir::IntrinsicHandlerEntry> intrinsicEntry =2744 fir::lookupIntrinsicHandler(callContext.getBuilder(),2745 callContext.getProcedureName(),2746 callContext.resultType);2747 assert(intrinsicEntry.has_value() &&2748 "intrinsic with custom handling for OPTIONAL arguments must have "2749 "lowering entries");2750 // All of the custom intrinsic elementals with custom handling are pure2751 // functions2752 return ElementalIntrinsicCallBuilder{intrinsic, *intrinsicEntry,2753 /*isFunction=*/true}2754 .genElementalCall(operands, /*isImpure=*/false, callContext);2755}2756 2757// Lower a reference to an intrinsic procedure with custom optional handling2758static std::optional<hlfir::EntityWithAttributes>2759genCustomIntrinsicRef(const Fortran::evaluate::SpecificIntrinsic *intrinsic,2760 CallContext &callContext) {2761 assert(!callContext.isElementalProcWithArrayArgs() &&2762 "Needs to be run through ElementalIntrinsicCallBuilder first");2763 mlir::Location loc = callContext.loc;2764 fir::FirOpBuilder &builder = callContext.getBuilder();2765 auto &converter = callContext.converter;2766 auto &stmtCtx = callContext.stmtCtx;2767 assert(intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling(2768 callContext.procRef, *intrinsic, converter));2769 Fortran::lower::PreparedActualArguments loweredActuals;2770 2771 // callback for optional arguments2772 auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) {2773 hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR(2774 loc, converter, expr, callContext.symMap, callContext.stmtCtx);2775 mlir::Value isPresent =2776 genIsPresentIfArgMaybeAbsent(loc, actual, expr, callContext,2777 /*passAsAllocatableOrPointer*/ false)2778 .value();2779 loweredActuals.emplace_back(2780 Fortran::lower::PreparedActualArgument{actual, {isPresent}});2781 };2782 2783 // callback for non-optional arguments2784 auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr,2785 fir::LowerIntrinsicArgAs lowerAs) {2786 auto getActualFortranElementType = [&]() -> mlir::Type {2787 return hlfir::getFortranElementType(converter.genType(expr));2788 };2789 hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR(2790 loc, converter, expr, callContext.symMap, callContext.stmtCtx);2791 std::optional<fir::ExtendedValue> exv;2792 switch (lowerAs) {2793 case fir::LowerIntrinsicArgAs::Value:2794 exv = Fortran::lower::convertToValue(loc, converter, actual, stmtCtx);2795 break;2796 case fir::LowerIntrinsicArgAs::Addr:2797 exv = Fortran::lower::convertToAddress(loc, converter, actual, stmtCtx,2798 getActualFortranElementType());2799 break;2800 case fir::LowerIntrinsicArgAs::Box:2801 exv = Fortran::lower::convertToBox(loc, converter, actual, stmtCtx,2802 getActualFortranElementType());2803 break;2804 case fir::LowerIntrinsicArgAs::Inquired:2805 exv = Fortran::lower::translateToExtendedValue(loc, builder, actual,2806 stmtCtx);2807 break;2808 }2809 if (!exv)2810 llvm_unreachable("bad switch");2811 actual = extendedValueToHlfirEntity(loc, builder, exv.value(),2812 "tmp.custom_intrinsic_arg");2813 loweredActuals.emplace_back(Fortran::lower::PreparedActualArgument{2814 actual, /*isPresent=*/std::nullopt});2815 };2816 2817 Fortran::lower::prepareCustomIntrinsicArgument(2818 callContext.procRef, *intrinsic, callContext.resultType,2819 prepareOptionalArg, prepareOtherArg, converter);2820 2821 return genCustomIntrinsicRefCore(loweredActuals, intrinsic, callContext);2822}2823 2824/// Lower an intrinsic procedure reference.2825/// \p intrinsic is null if this is an intrinsic module procedure that must be2826/// lowered as if it were an intrinsic module procedure (like C_LOC which is a2827/// procedure from intrinsic module iso_c_binding). Otherwise, \p intrinsic2828/// must not be null.2829 2830static std::optional<hlfir::EntityWithAttributes>2831genIntrinsicRef(const Fortran::evaluate::SpecificIntrinsic *intrinsic,2832 const fir::IntrinsicHandlerEntry &intrinsicEntry,2833 CallContext &callContext) {2834 mlir::Location loc = callContext.loc;2835 Fortran::lower::PreparedActualArguments loweredActuals;2836 const fir::IntrinsicArgumentLoweringRules *argLowering =2837 intrinsicEntry.getArgumentLoweringRules();2838 for (const auto &arg : llvm::enumerate(callContext.procRef.arguments())) {2839 2840 if (!arg.value()) {2841 // Absent optional.2842 loweredActuals.push_back(std::nullopt);2843 continue;2844 }2845 auto *expr =2846 Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg.value());2847 if (!expr) {2848 // TYPE(*) dummy. They are only allowed as argument of a few intrinsics2849 // that do not take optional arguments: see Fortran 2018 standard C710.2850 const Fortran::evaluate::Symbol *assumedTypeSym =2851 arg.value()->GetAssumedTypeDummy();2852 if (!assumedTypeSym)2853 fir::emitFatalError(loc,2854 "expected assumed-type symbol as actual argument");2855 std::optional<fir::FortranVariableOpInterface> var =2856 callContext.symMap.lookupVariableDefinition(*assumedTypeSym);2857 if (!var)2858 fir::emitFatalError(loc, "assumed-type symbol was not lowered");2859 assert(2860 (!argLowering ||2861 !fir::lowerIntrinsicArgumentAs(*argLowering, arg.index())2862 .handleDynamicOptional) &&2863 "TYPE(*) are not expected to appear as optional intrinsic arguments");2864 loweredActuals.push_back(Fortran::lower::PreparedActualArgument{2865 hlfir::Entity{*var}, /*isPresent=*/std::nullopt});2866 continue;2867 }2868 // arguments of bitwise comparison functions may not have nsw flag2869 // even if -fno-wrapv is enabled2870 mlir::arith::IntegerOverflowFlags iofBackup{};2871 auto isBitwiseComparison = [](const std::string intrinsicName) -> bool {2872 if (intrinsicName == "bge" || intrinsicName == "bgt" ||2873 intrinsicName == "ble" || intrinsicName == "blt")2874 return true;2875 return false;2876 };2877 if (isBitwiseComparison(callContext.getProcedureName())) {2878 iofBackup = callContext.getBuilder().getIntegerOverflowFlags();2879 callContext.getBuilder().setIntegerOverflowFlags(2880 mlir::arith::IntegerOverflowFlags::none);2881 }2882 auto loweredActual = Fortran::lower::convertExprToHLFIR(2883 loc, callContext.converter, *expr, callContext.symMap,2884 callContext.stmtCtx);2885 if (isBitwiseComparison(callContext.getProcedureName()))2886 callContext.getBuilder().setIntegerOverflowFlags(iofBackup);2887 2888 std::optional<mlir::Value> isPresent;2889 if (argLowering) {2890 fir::ArgLoweringRule argRules =2891 fir::lowerIntrinsicArgumentAs(*argLowering, arg.index());2892 if (argRules.handleDynamicOptional)2893 isPresent =2894 genIsPresentIfArgMaybeAbsent(loc, loweredActual, *expr, callContext,2895 /*passAsAllocatableOrPointer=*/false);2896 }2897 loweredActuals.push_back(2898 Fortran::lower::PreparedActualArgument{loweredActual, isPresent});2899 }2900 2901 if (callContext.isElementalProcWithArrayArgs()) {2902 // All intrinsic elemental functions are pure.2903 const bool isFunction = callContext.resultType.has_value();2904 return ElementalIntrinsicCallBuilder{intrinsic, intrinsicEntry, isFunction}2905 .genElementalCall(loweredActuals, /*isImpure=*/!isFunction,2906 callContext);2907 }2908 std::optional<hlfir::EntityWithAttributes> result = genHLFIRIntrinsicRefCore(2909 loweredActuals, intrinsic, intrinsicEntry, callContext);2910 if (result && mlir::isa<hlfir::ExprType>(result->getType())) {2911 fir::FirOpBuilder *bldr = &callContext.getBuilder();2912 callContext.stmtCtx.attachCleanup(2913 [=]() { hlfir::DestroyOp::create(*bldr, loc, *result); });2914 }2915 return result;2916}2917 2918static std::optional<hlfir::EntityWithAttributes>2919genIntrinsicRef(const Fortran::evaluate::SpecificIntrinsic *intrinsic,2920 CallContext &callContext) {2921 mlir::Location loc = callContext.loc;2922 auto &converter = callContext.converter;2923 if (intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling(2924 callContext.procRef, *intrinsic, converter)) {2925 if (callContext.isElementalProcWithArrayArgs())2926 return genCustomElementalIntrinsicRef(intrinsic, callContext);2927 return genCustomIntrinsicRef(intrinsic, callContext);2928 }2929 std::optional<fir::IntrinsicHandlerEntry> intrinsicEntry =2930 fir::lookupIntrinsicHandler(callContext.getBuilder(),2931 callContext.getProcedureName(),2932 callContext.resultType);2933 if (!intrinsicEntry)2934 fir::crashOnMissingIntrinsic(loc, callContext.getProcedureName());2935 return genIntrinsicRef(intrinsic, *intrinsicEntry, callContext);2936}2937 2938/// Main entry point to lower procedure references, regardless of what they are.2939static std::optional<hlfir::EntityWithAttributes>2940genProcedureRef(CallContext &callContext) {2941 mlir::Location loc = callContext.loc;2942 fir::FirOpBuilder &builder = callContext.getBuilder();2943 if (auto *intrinsic = callContext.procRef.proc().GetSpecificIntrinsic())2944 return genIntrinsicRef(intrinsic, callContext);2945 // Intercept non BIND(C) module procedure reference that have lowering2946 // handlers defined for there name. Otherwise, lower them as user2947 // procedure calls and expect the implementation to be part of2948 // runtime libraries with the proper name mangling.2949 if (Fortran::lower::isIntrinsicModuleProcRef(callContext.procRef) &&2950 !callContext.isBindcCall())2951 if (std::optional<fir::IntrinsicHandlerEntry> intrinsicEntry =2952 fir::lookupIntrinsicHandler(builder, callContext.getProcedureName(),2953 callContext.resultType))2954 return genIntrinsicRef(nullptr, *intrinsicEntry, callContext);2955 2956 if (callContext.isStatementFunctionCall())2957 return genStmtFunctionRef(loc, callContext.converter, callContext.symMap,2958 callContext.stmtCtx, callContext.procRef);2959 2960 Fortran::lower::CallerInterface caller(callContext.procRef,2961 callContext.converter);2962 mlir::FunctionType callSiteType = caller.genFunctionType();2963 const bool isElemental = callContext.isElementalProcWithArrayArgs();2964 Fortran::lower::PreparedActualArguments loweredActuals;2965 // Lower the actual arguments2966 for (const Fortran::lower::CallInterface<2967 Fortran::lower::CallerInterface>::PassedEntity &arg :2968 caller.getPassedArguments())2969 if (const auto *actual = arg.entity) {2970 const auto *expr = actual->UnwrapExpr();2971 if (!expr) {2972 // TYPE(*) actual argument.2973 const Fortran::evaluate::Symbol *assumedTypeSym =2974 actual->GetAssumedTypeDummy();2975 if (!assumedTypeSym)2976 fir::emitFatalError(2977 loc, "expected assumed-type symbol as actual argument");2978 std::optional<fir::FortranVariableOpInterface> var =2979 callContext.symMap.lookupVariableDefinition(*assumedTypeSym);2980 if (!var)2981 fir::emitFatalError(loc, "assumed-type symbol was not lowered");2982 hlfir::Entity actual{*var};2983 std::optional<mlir::Value> isPresent;2984 if (arg.isOptional()) {2985 // Passing an optional TYPE(*) to an optional TYPE(*). Note that2986 // TYPE(*) cannot be ALLOCATABLE/POINTER (C709) so there is no2987 // need to cover the case of passing an ALLOCATABLE/POINTER to an2988 // OPTIONAL.2989 isPresent = fir::IsPresentOp::create(builder, loc,2990 builder.getI1Type(), actual)2991 .getResult();2992 }2993 loweredActuals.push_back(Fortran::lower::PreparedActualArgument{2994 hlfir::Entity{*var}, isPresent});2995 continue;2996 }2997 2998 if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(2999 *expr)) {3000 if ((arg.passBy !=3001 Fortran::lower::CallerInterface::PassEntityBy::MutableBox) &&3002 (arg.passBy !=3003 Fortran::lower::CallerInterface::PassEntityBy::BoxProcRef)) {3004 assert(3005 arg.isOptional() &&3006 "NULL must be passed only to pointer, allocatable, or OPTIONAL");3007 // Trying to lower NULL() outside of any context would lead to3008 // trouble. NULL() here is equivalent to not providing the3009 // actual argument.3010 loweredActuals.emplace_back(std::nullopt);3011 continue;3012 }3013 }3014 3015 if (isElemental && !arg.hasValueAttribute() &&3016 Fortran::evaluate::IsVariable(*expr) &&3017 Fortran::evaluate::HasVectorSubscript(*expr)) {3018 // Vector subscripted arguments are copied in calls, except in elemental3019 // calls without VALUE attribute where Fortran 2018 15.5.2.4 point 213020 // does not apply and the address of each element must be passed.3021 hlfir::ElementalAddrOp elementalAddr =3022 Fortran::lower::convertVectorSubscriptedExprToElementalAddr(3023 loc, callContext.converter, *expr, callContext.symMap,3024 callContext.stmtCtx);3025 loweredActuals.emplace_back(3026 Fortran::lower::PreparedActualArgument{elementalAddr});3027 continue;3028 }3029 3030 auto loweredActual = Fortran::lower::convertExprToHLFIR(3031 loc, callContext.converter, *expr, callContext.symMap,3032 callContext.stmtCtx);3033 std::optional<mlir::Value> isPresent;3034 if (arg.isOptional())3035 isPresent = genIsPresentIfArgMaybeAbsent(3036 loc, loweredActual, *expr, callContext,3037 arg.passBy ==3038 Fortran::lower::CallerInterface::PassEntityBy::MutableBox);3039 3040 loweredActuals.emplace_back(3041 Fortran::lower::PreparedActualArgument{loweredActual, isPresent});3042 } else {3043 // Optional dummy argument for which there is no actual argument.3044 loweredActuals.emplace_back(std::nullopt);3045 }3046 if (isElemental) {3047 bool isImpure = false;3048 if (const Fortran::semantics::Symbol *procSym =3049 callContext.procRef.proc().GetSymbol())3050 isImpure = !Fortran::semantics::IsPureProcedure(*procSym);3051 return ElementalUserCallBuilder{caller, callSiteType}.genElementalCall(3052 loweredActuals, isImpure, callContext);3053 }3054 return genUserCall(loweredActuals, caller, callSiteType, callContext);3055}3056 3057hlfir::Entity Fortran::lower::PreparedActualArgument::getActual(3058 mlir::Location loc, fir::FirOpBuilder &builder) const {3059 if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual)) {3060 if (oneBasedElementalIndices)3061 return hlfir::getElementAt(loc, builder, *actualEntity,3062 *oneBasedElementalIndices);3063 return *actualEntity;3064 }3065 assert(oneBasedElementalIndices && "expect elemental context");3066 hlfir::ElementalAddrOp elementalAddr =3067 std::get<hlfir::ElementalAddrOp>(actual);3068 mlir::IRMapping mapper;3069 auto alwaysFalse = [](hlfir::ElementalOp) -> bool { return false; };3070 mlir::Value addr = hlfir::inlineElementalOp(3071 loc, builder, elementalAddr, *oneBasedElementalIndices, mapper,3072 /*mustRecursivelyInline=*/alwaysFalse);3073 assert(elementalAddr.getCleanup().empty() && "no clean-up expected");3074 elementalAddr.erase();3075 return hlfir::Entity{addr};3076}3077 3078bool Fortran::lower::isIntrinsicModuleProcRef(3079 const Fortran::evaluate::ProcedureRef &procRef) {3080 const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol();3081 if (!symbol)3082 return false;3083 const Fortran::semantics::Symbol *module =3084 symbol->GetUltimate().owner().GetSymbol();3085 return module && module->attrs().test(Fortran::semantics::Attr::INTRINSIC);3086}3087 3088static bool isInWhereMaskedExpression(fir::FirOpBuilder &builder) {3089 // The MASK of the outer WHERE is not masked itself.3090 mlir::Operation *op = builder.getRegion().getParentOp();3091 return op && op->getParentOfType<hlfir::WhereOp>();3092}3093 3094std::optional<hlfir::EntityWithAttributes> Fortran::lower::convertCallToHLFIR(3095 mlir::Location loc, Fortran::lower::AbstractConverter &converter,3096 const evaluate::ProcedureRef &procRef, std::optional<mlir::Type> resultType,3097 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {3098 auto &builder = converter.getFirOpBuilder();3099 if (resultType && !procRef.IsElemental() &&3100 isInWhereMaskedExpression(builder) &&3101 !builder.getRegion().getParentOfType<hlfir::ExactlyOnceOp>()) {3102 // Non elemental calls inside a where-assignment-stmt must be executed3103 // exactly once without mask control. Lower them in a special region so that3104 // this can be enforced whenscheduling forall/where expression evaluations.3105 Fortran::lower::StatementContext localStmtCtx;3106 mlir::Type bogusType = builder.getIndexType();3107 auto exactlyOnce = hlfir::ExactlyOnceOp::create(builder, loc, bogusType);3108 mlir::Block *block = builder.createBlock(&exactlyOnce.getBody());3109 builder.setInsertionPointToStart(block);3110 CallContext callContext(procRef, resultType, loc, converter, symMap,3111 localStmtCtx);3112 std::optional<hlfir::EntityWithAttributes> res =3113 genProcedureRef(callContext);3114 assert(res.has_value() && "must be a function");3115 auto yield = hlfir::YieldOp::create(builder, loc, *res);3116 Fortran::lower::genCleanUpInRegionIfAny(loc, builder, yield.getCleanup(),3117 localStmtCtx);3118 builder.setInsertionPointAfter(exactlyOnce);3119 exactlyOnce->getResult(0).setType(res->getType());3120 if (hlfir::isFortranValue(exactlyOnce.getResult()))3121 return hlfir::EntityWithAttributes{exactlyOnce.getResult()};3122 // Create hlfir.declare for the result to satisfy3123 // hlfir::EntityWithAttributes requirements.3124 auto [exv, cleanup] = hlfir::translateToExtendedValue(3125 loc, builder, hlfir::Entity{exactlyOnce});3126 assert(!cleanup && "resut is a variable");3127 return hlfir::genDeclare(loc, builder, exv, ".func.pointer.result",3128 fir::FortranVariableFlagsAttr{});3129 }3130 CallContext callContext(procRef, resultType, loc, converter, symMap, stmtCtx);3131 return genProcedureRef(callContext);3132}3133 3134void Fortran::lower::convertUserDefinedAssignmentToHLFIR(3135 mlir::Location loc, Fortran::lower::AbstractConverter &converter,3136 const evaluate::ProcedureRef &procRef, hlfir::Entity lhs, hlfir::Entity rhs,3137 Fortran::lower::SymMap &symMap) {3138 Fortran::lower::StatementContext definedAssignmentContext;3139 // For defined assignment, don't use regular copy-in/copy-out mechanism:3140 // defined assignment generates hlfir.region_assign construct, and this3141 // construct automatically handles any copy-in.3142 CallContext callContext(procRef, /*resultType=*/std::nullopt, loc, converter,3143 symMap, definedAssignmentContext, /*doCopyIn=*/false);3144 Fortran::lower::CallerInterface caller(procRef, converter);3145 mlir::FunctionType callSiteType = caller.genFunctionType();3146 PreparedActualArgument preparedLhs{lhs, /*isPresent=*/std::nullopt};3147 PreparedActualArgument preparedRhs{rhs, /*isPresent=*/std::nullopt};3148 PreparedActualArguments loweredActuals{preparedLhs, preparedRhs};3149 genUserCall(loweredActuals, caller, callSiteType, callContext);3150 return;3151}3152