1011b2af0SJean Perier //===-- ConvertCall.cpp ---------------------------------------------------===// 2011b2af0SJean Perier // 3011b2af0SJean Perier // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4011b2af0SJean Perier // See https://llvm.org/LICENSE.txt for license information. 5011b2af0SJean Perier // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6011b2af0SJean Perier // 7011b2af0SJean Perier //===----------------------------------------------------------------------===// 8011b2af0SJean Perier // 9011b2af0SJean Perier // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ 10011b2af0SJean Perier // 11011b2af0SJean Perier //===----------------------------------------------------------------------===// 12011b2af0SJean Perier 13011b2af0SJean Perier #include "flang/Lower/ConvertCall.h" 14e5cb6da7SjeanPerier #include "flang/Lower/Allocatable.h" 15e78e4a17SJean Perier #include "flang/Lower/ConvertExprToHLFIR.h" 16c373f581SjeanPerier #include "flang/Lower/ConvertProcedureDesignator.h" 17011b2af0SJean Perier #include "flang/Lower/ConvertVariable.h" 18b013ebe0SJean Perier #include "flang/Lower/CustomIntrinsicCall.h" 19d2d21301STom Eccles #include "flang/Lower/HlfirIntrinsics.h" 20011b2af0SJean Perier #include "flang/Lower/StatementContext.h" 21011b2af0SJean Perier #include "flang/Lower/SymbolMap.h" 22011b2af0SJean Perier #include "flang/Optimizer/Builder/BoxValue.h" 23011b2af0SJean Perier #include "flang/Optimizer/Builder/Character.h" 24011b2af0SJean Perier #include "flang/Optimizer/Builder/FIRBuilder.h" 259facbb69STom Eccles #include "flang/Optimizer/Builder/HLFIRTools.h" 266dcb31deSTom Eccles #include "flang/Optimizer/Builder/IntrinsicCall.h" 27011b2af0SJean Perier #include "flang/Optimizer/Builder/LowLevelIntrinsics.h" 28011b2af0SJean Perier #include "flang/Optimizer/Builder/MutableBox.h" 2997492fd1SValentin Clement #include "flang/Optimizer/Builder/Runtime/Derived.h" 30011b2af0SJean Perier #include "flang/Optimizer/Builder/Todo.h" 3145daa4fdSValentin Clement (バレンタイン クレメン) #include "flang/Optimizer/Dialect/CUF/CUFOps.h" 32011b2af0SJean Perier #include "flang/Optimizer/Dialect/FIROpsSupport.h" 33e78e4a17SJean Perier #include "flang/Optimizer/HLFIR/HLFIROps.h" 348c2ed5ccSjeanPerier #include "mlir/IR/IRMapping.h" 359facbb69STom Eccles #include "llvm/Support/CommandLine.h" 36011b2af0SJean Perier #include "llvm/Support/Debug.h" 374d4d4785SKazu Hirata #include <optional> 38011b2af0SJean Perier 39011b2af0SJean Perier #define DEBUG_TYPE "flang-lower-expr" 40011b2af0SJean Perier 419facbb69STom Eccles static llvm::cl::opt<bool> useHlfirIntrinsicOps( 429facbb69STom Eccles "use-hlfir-intrinsic-ops", llvm::cl::init(true), 436ed4a8b9SJean Perier llvm::cl::desc("Lower via HLFIR transformational intrinsic operations such " 446ed4a8b9SJean Perier "as hlfir.sum")); 459facbb69STom Eccles 4667402fe5SjeanPerier static constexpr char tempResultName[] = ".tmp.func_result"; 4767402fe5SjeanPerier 48011b2af0SJean Perier /// Helper to package a Value and its properties into an ExtendedValue. 49011b2af0SJean Perier static fir::ExtendedValue toExtendedValue(mlir::Location loc, mlir::Value base, 50011b2af0SJean Perier llvm::ArrayRef<mlir::Value> extents, 51011b2af0SJean Perier llvm::ArrayRef<mlir::Value> lengths) { 52011b2af0SJean Perier mlir::Type type = base.getType(); 53fac349a1SChristian Sigg if (mlir::isa<fir::BaseBoxType>(type)) 54011b2af0SJean Perier return fir::BoxValue(base, /*lbounds=*/{}, lengths, extents); 55011b2af0SJean Perier type = fir::unwrapRefType(type); 56fac349a1SChristian Sigg if (mlir::isa<fir::BaseBoxType>(type)) 57011b2af0SJean Perier return fir::MutableBoxValue(base, lengths, /*mutableProperties*/ {}); 58fac349a1SChristian Sigg if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(type)) { 59011b2af0SJean Perier if (seqTy.getDimension() != extents.size()) 60011b2af0SJean Perier fir::emitFatalError(loc, "incorrect number of extents for array"); 61fac349a1SChristian Sigg if (mlir::isa<fir::CharacterType>(seqTy.getEleTy())) { 62011b2af0SJean Perier if (lengths.empty()) 63011b2af0SJean Perier fir::emitFatalError(loc, "missing length for character"); 64011b2af0SJean Perier assert(lengths.size() == 1); 65011b2af0SJean Perier return fir::CharArrayBoxValue(base, lengths[0], extents); 66011b2af0SJean Perier } 67011b2af0SJean Perier return fir::ArrayBoxValue(base, extents); 68011b2af0SJean Perier } 69fac349a1SChristian Sigg if (mlir::isa<fir::CharacterType>(type)) { 70011b2af0SJean Perier if (lengths.empty()) 71011b2af0SJean Perier fir::emitFatalError(loc, "missing length for character"); 72011b2af0SJean Perier assert(lengths.size() == 1); 73011b2af0SJean Perier return fir::CharBoxValue(base, lengths[0]); 74011b2af0SJean Perier } 75011b2af0SJean Perier return base; 76011b2af0SJean Perier } 77011b2af0SJean Perier 78011b2af0SJean Perier /// Lower a type(C_PTR/C_FUNPTR) argument with VALUE attribute into a 79011b2af0SJean Perier /// reference. A C pointer can correspond to a Fortran dummy argument of type 80011b2af0SJean Perier /// C_PTR with the VALUE attribute. (see 18.3.6 note 3). 81882e5f7bSSlava Zakharin static mlir::Value genRecordCPtrValueArg(fir::FirOpBuilder &builder, 82882e5f7bSSlava Zakharin mlir::Location loc, mlir::Value rec, 83882e5f7bSSlava Zakharin mlir::Type ty) { 84011b2af0SJean Perier mlir::Value cAddr = fir::factory::genCPtrOrCFunptrAddr(builder, loc, rec, ty); 85011b2af0SJean Perier mlir::Value cVal = builder.create<fir::LoadOp>(loc, cAddr); 86011b2af0SJean Perier return builder.createConvert(loc, cAddr.getType(), cVal); 87011b2af0SJean Perier } 88011b2af0SJean Perier 89011b2af0SJean Perier // Find the argument that corresponds to the host associations. 90011b2af0SJean Perier // Verify some assumptions about how the signature was built here. 91011b2af0SJean Perier [[maybe_unused]] static unsigned findHostAssocTuplePos(mlir::func::FuncOp fn) { 92011b2af0SJean Perier // Scan the argument list from last to first as the host associations are 93011b2af0SJean Perier // appended for now. 94011b2af0SJean Perier for (unsigned i = fn.getNumArguments(); i > 0; --i) 95011b2af0SJean Perier if (fn.getArgAttr(i - 1, fir::getHostAssocAttrName())) { 96011b2af0SJean Perier // Host assoc tuple must be last argument (for now). 97011b2af0SJean Perier assert(i == fn.getNumArguments() && "tuple must be last"); 98011b2af0SJean Perier return i - 1; 99011b2af0SJean Perier } 100011b2af0SJean Perier llvm_unreachable("anyFuncArgsHaveAttr failed"); 101011b2af0SJean Perier } 102011b2af0SJean Perier 103011b2af0SJean Perier mlir::Value 104011b2af0SJean Perier Fortran::lower::argumentHostAssocs(Fortran::lower::AbstractConverter &converter, 105011b2af0SJean Perier mlir::Value arg) { 106011b2af0SJean Perier if (auto addr = mlir::dyn_cast_or_null<fir::AddrOfOp>(arg.getDefiningOp())) { 107011b2af0SJean Perier auto &builder = converter.getFirOpBuilder(); 108011b2af0SJean Perier if (auto funcOp = builder.getNamedFunction(addr.getSymbol())) 109011b2af0SJean Perier if (fir::anyFuncArgsHaveAttr(funcOp, fir::getHostAssocAttrName())) 110011b2af0SJean Perier return converter.hostAssocTupleValue(); 111011b2af0SJean Perier } 112011b2af0SJean Perier return {}; 113011b2af0SJean Perier } 114011b2af0SJean Perier 11592e904b9SJean Perier static bool mustCastFuncOpToCopeWithImplicitInterfaceMismatch( 11692e904b9SJean Perier mlir::Location loc, Fortran::lower::AbstractConverter &converter, 11792e904b9SJean Perier mlir::FunctionType callSiteType, mlir::FunctionType funcOpType) { 11892e904b9SJean Perier // Deal with argument number mismatch by making a function pointer so 11992e904b9SJean Perier // that function type cast can be inserted. Do not emit a warning here 12092e904b9SJean Perier // because this can happen in legal program if the function is not 12192e904b9SJean Perier // defined here and it was first passed as an argument without any more 12292e904b9SJean Perier // information. 12392e904b9SJean Perier if (callSiteType.getNumResults() != funcOpType.getNumResults() || 12492e904b9SJean Perier callSiteType.getNumInputs() != funcOpType.getNumInputs()) 12592e904b9SJean Perier return true; 12692e904b9SJean Perier 12792e904b9SJean Perier // Implicit interface result type mismatch are not standard Fortran, but 12892e904b9SJean Perier // some compilers are not complaining about it. The front end is not 12992e904b9SJean Perier // protecting lowering from this currently. Support this with a 13092e904b9SJean Perier // discouraging warning. 13192e904b9SJean Perier // Cast the actual function to the current caller implicit type because 13292e904b9SJean Perier // that is the behavior we would get if we could not see the definition. 13392e904b9SJean Perier if (callSiteType.getResults() != funcOpType.getResults()) { 13492e904b9SJean Perier LLVM_DEBUG(mlir::emitWarning( 13592e904b9SJean Perier loc, "a return type mismatch is not standard compliant and may " 13692e904b9SJean Perier "lead to undefined behavior.")); 13792e904b9SJean Perier return true; 13892e904b9SJean Perier } 13992e904b9SJean Perier 14092e904b9SJean Perier // In HLFIR, there is little attempt to cope with implicit interface 14192e904b9SJean Perier // mismatch on the arguments. The argument are always prepared according 14292e904b9SJean Perier // to the implicit interface. Cast the actual function if any of the 14392e904b9SJean Perier // argument mismatch cannot be dealt with a simple fir.convert. 14492e904b9SJean Perier if (converter.getLoweringOptions().getLowerToHighLevelFIR()) 14592e904b9SJean Perier for (auto [actualType, dummyType] : 14692e904b9SJean Perier llvm::zip(callSiteType.getInputs(), funcOpType.getInputs())) 14792e904b9SJean Perier if (actualType != dummyType && 14892e904b9SJean Perier !fir::ConvertOp::canBeConverted(actualType, dummyType)) 14992e904b9SJean Perier return true; 15092e904b9SJean Perier return false; 15192e904b9SJean Perier } 15292e904b9SJean Perier 1537106389bSValentin Clement (バレンタイン クレメン) static mlir::Value readDim3Value(fir::FirOpBuilder &builder, mlir::Location loc, 1547106389bSValentin Clement (バレンタイン クレメン) mlir::Value dim3Addr, llvm::StringRef comp) { 1557106389bSValentin Clement (バレンタイン クレメン) mlir::Type i32Ty = builder.getI32Type(); 1567106389bSValentin Clement (バレンタイン クレメン) mlir::Type refI32Ty = fir::ReferenceType::get(i32Ty); 1577106389bSValentin Clement (バレンタイン クレメン) llvm::SmallVector<mlir::Value> lenParams; 1587106389bSValentin Clement (バレンタイン クレメン) 1597106389bSValentin Clement (バレンタイン クレメン) mlir::Value designate = builder.create<hlfir::DesignateOp>( 1607106389bSValentin Clement (バレンタイン クレメン) loc, refI32Ty, dim3Addr, /*component=*/comp, 1617106389bSValentin Clement (バレンタイン クレメン) /*componentShape=*/mlir::Value{}, hlfir::DesignateOp::Subscripts{}, 1627106389bSValentin Clement (バレンタイン クレメン) /*substring=*/mlir::ValueRange{}, /*complexPartAttr=*/std::nullopt, 1637106389bSValentin Clement (バレンタイン クレメン) mlir::Value{}, lenParams); 1647106389bSValentin Clement (バレンタイン クレメン) 1657106389bSValentin Clement (バレンタイン クレメン) return hlfir::loadTrivialScalar(loc, builder, hlfir::Entity{designate}); 1667106389bSValentin Clement (バレンタイン クレメン) } 1677106389bSValentin Clement (バレンタイン クレメン) 1688eee2360SjeanPerier static mlir::Value remapActualToDummyDescriptor( 1698eee2360SjeanPerier mlir::Location loc, Fortran::lower::AbstractConverter &converter, 1708eee2360SjeanPerier Fortran::lower::SymMap &symMap, 1718eee2360SjeanPerier const Fortran::lower::CallerInterface::PassedEntity &arg, 1728eee2360SjeanPerier Fortran::lower::CallerInterface &caller, bool isBindcCall) { 1738eee2360SjeanPerier fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 1748eee2360SjeanPerier mlir::IndexType idxTy = builder.getIndexType(); 1758eee2360SjeanPerier mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0); 1768eee2360SjeanPerier Fortran::lower::StatementContext localStmtCtx; 1778eee2360SjeanPerier auto lowerSpecExpr = [&](const auto &expr, 1788eee2360SjeanPerier bool isAssumedSizeExtent) -> mlir::Value { 1798eee2360SjeanPerier mlir::Value convertExpr = builder.createConvert( 1808eee2360SjeanPerier loc, idxTy, fir::getBase(converter.genExprValue(expr, localStmtCtx))); 1818eee2360SjeanPerier if (isAssumedSizeExtent) 1828eee2360SjeanPerier return convertExpr; 1838eee2360SjeanPerier return fir::factory::genMaxWithZero(builder, loc, convertExpr); 1848eee2360SjeanPerier }; 1858eee2360SjeanPerier bool mapSymbols = caller.mustMapInterfaceSymbolsForDummyArgument(arg); 1868eee2360SjeanPerier if (mapSymbols) { 1878eee2360SjeanPerier symMap.pushScope(); 1888eee2360SjeanPerier const Fortran::semantics::Symbol *sym = caller.getDummySymbol(arg); 1898eee2360SjeanPerier assert(sym && "call must have explicit interface to map interface symbols"); 1908eee2360SjeanPerier Fortran::lower::mapCallInterfaceSymbolsForDummyArgument(converter, caller, 1918eee2360SjeanPerier symMap, *sym); 1928eee2360SjeanPerier } 1938eee2360SjeanPerier llvm::SmallVector<mlir::Value> extents; 1948eee2360SjeanPerier llvm::SmallVector<mlir::Value> lengths; 1958eee2360SjeanPerier mlir::Type dummyBoxType = caller.getDummyArgumentType(arg); 1968eee2360SjeanPerier mlir::Type dummyBaseType = fir::unwrapPassByRefType(dummyBoxType); 197fac349a1SChristian Sigg if (mlir::isa<fir::SequenceType>(dummyBaseType)) 1988eee2360SjeanPerier caller.walkDummyArgumentExtents( 1998eee2360SjeanPerier arg, [&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) { 2008eee2360SjeanPerier extents.emplace_back(lowerSpecExpr(e, isAssumedSizeExtent)); 2018eee2360SjeanPerier }); 2028eee2360SjeanPerier mlir::Value shape; 2038eee2360SjeanPerier if (!extents.empty()) { 2048eee2360SjeanPerier if (isBindcCall) { 2058eee2360SjeanPerier // Preserve zero lower bounds (see F'2023 18.5.3). 2068eee2360SjeanPerier llvm::SmallVector<mlir::Value> lowerBounds(extents.size(), zero); 2078eee2360SjeanPerier shape = builder.genShape(loc, lowerBounds, extents); 2088eee2360SjeanPerier } else { 2098eee2360SjeanPerier shape = builder.genShape(loc, extents); 2108eee2360SjeanPerier } 2118eee2360SjeanPerier } 2128eee2360SjeanPerier 2138eee2360SjeanPerier hlfir::Entity explicitArgument = hlfir::Entity{caller.getInput(arg)}; 2148eee2360SjeanPerier mlir::Type dummyElementType = fir::unwrapSequenceType(dummyBaseType); 2158eee2360SjeanPerier if (auto recType = llvm::dyn_cast<fir::RecordType>(dummyElementType)) 2168eee2360SjeanPerier if (recType.getNumLenParams() > 0) 2178eee2360SjeanPerier TODO(loc, "sequence association of length parameterized derived type " 2188eee2360SjeanPerier "dummy arguments"); 2198eee2360SjeanPerier if (fir::isa_char(dummyElementType)) 2208eee2360SjeanPerier lengths.emplace_back(hlfir::genCharLength(loc, builder, explicitArgument)); 2218eee2360SjeanPerier mlir::Value baseAddr = 2228eee2360SjeanPerier hlfir::genVariableRawAddress(loc, builder, explicitArgument); 2238eee2360SjeanPerier baseAddr = builder.createConvert(loc, fir::ReferenceType::get(dummyBaseType), 2248eee2360SjeanPerier baseAddr); 2258eee2360SjeanPerier mlir::Value mold; 2268eee2360SjeanPerier if (fir::isPolymorphicType(dummyBoxType)) 2278eee2360SjeanPerier mold = explicitArgument; 2288eee2360SjeanPerier mlir::Value remapped = 2298eee2360SjeanPerier builder.create<fir::EmboxOp>(loc, dummyBoxType, baseAddr, shape, 2308eee2360SjeanPerier /*slice=*/mlir::Value{}, lengths, mold); 2318eee2360SjeanPerier if (mapSymbols) 2328eee2360SjeanPerier symMap.popScope(); 2338eee2360SjeanPerier return remapped; 2348eee2360SjeanPerier } 2358eee2360SjeanPerier 2368eee2360SjeanPerier /// Create a descriptor for sequenced associated descriptor that are passed 2378eee2360SjeanPerier /// by descriptor. Sequence association (F'2023 15.5.2.12) implies that the 2388eee2360SjeanPerier /// dummy shape and rank need to not be the same as the actual argument. This 2398eee2360SjeanPerier /// helper creates a descriptor based on the dummy shape and rank (sequence 2408eee2360SjeanPerier /// association can only happen with explicit and assumed-size array) so that it 2418eee2360SjeanPerier /// is safe to assume the rank of the incoming descriptor inside the callee. 2428eee2360SjeanPerier /// This helper must be called once all the actual arguments have been lowered 2438eee2360SjeanPerier /// and placed inside "caller". Copy-in/copy-out must already have been 2448eee2360SjeanPerier /// generated if needed using the actual argument shape (the dummy shape may be 2458eee2360SjeanPerier /// assumed-size). 2468eee2360SjeanPerier static void remapActualToDummyDescriptors( 2478eee2360SjeanPerier mlir::Location loc, Fortran::lower::AbstractConverter &converter, 2488eee2360SjeanPerier Fortran::lower::SymMap &symMap, 2498eee2360SjeanPerier const Fortran::lower::PreparedActualArguments &loweredActuals, 2508eee2360SjeanPerier Fortran::lower::CallerInterface &caller, bool isBindcCall) { 2518eee2360SjeanPerier fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 2528eee2360SjeanPerier for (auto [preparedActual, arg] : 2538eee2360SjeanPerier llvm::zip(loweredActuals, caller.getPassedArguments())) { 2548eee2360SjeanPerier if (arg.isSequenceAssociatedDescriptor()) { 2558eee2360SjeanPerier if (!preparedActual.value().handleDynamicOptional()) { 2568eee2360SjeanPerier mlir::Value remapped = remapActualToDummyDescriptor( 2578eee2360SjeanPerier loc, converter, symMap, arg, caller, isBindcCall); 2588eee2360SjeanPerier caller.placeInput(arg, remapped); 2598eee2360SjeanPerier } else { 2608eee2360SjeanPerier // Absent optional actual argument descriptor cannot be read and 2618eee2360SjeanPerier // remapped unconditionally. 2628eee2360SjeanPerier mlir::Type dummyType = caller.getDummyArgumentType(arg); 2638eee2360SjeanPerier mlir::Value isPresent = preparedActual.value().getIsPresent(); 2648eee2360SjeanPerier auto &argLambdaCapture = arg; 2658eee2360SjeanPerier mlir::Value remapped = 2668eee2360SjeanPerier builder 2678eee2360SjeanPerier .genIfOp(loc, {dummyType}, isPresent, 2688eee2360SjeanPerier /*withElseRegion=*/true) 2698eee2360SjeanPerier .genThen([&]() { 2708eee2360SjeanPerier mlir::Value newBox = remapActualToDummyDescriptor( 2718eee2360SjeanPerier loc, converter, symMap, argLambdaCapture, caller, 2728eee2360SjeanPerier isBindcCall); 2738eee2360SjeanPerier builder.create<fir::ResultOp>(loc, newBox); 2748eee2360SjeanPerier }) 2758eee2360SjeanPerier .genElse([&]() { 2768eee2360SjeanPerier mlir::Value absent = 2778eee2360SjeanPerier builder.create<fir::AbsentOp>(loc, dummyType); 2788eee2360SjeanPerier builder.create<fir::ResultOp>(loc, absent); 2798eee2360SjeanPerier }) 2808eee2360SjeanPerier .getResults()[0]; 2818eee2360SjeanPerier caller.placeInput(arg, remapped); 2828eee2360SjeanPerier } 2838eee2360SjeanPerier } 2848eee2360SjeanPerier } 2858eee2360SjeanPerier } 2868eee2360SjeanPerier 287*cd7e6539SjeanPerier std::pair<Fortran::lower::LoweredResult, bool> 288*cd7e6539SjeanPerier Fortran::lower::genCallOpAndResult( 289011b2af0SJean Perier mlir::Location loc, Fortran::lower::AbstractConverter &converter, 290011b2af0SJean Perier Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx, 291011b2af0SJean Perier Fortran::lower::CallerInterface &caller, mlir::FunctionType callSiteType, 292ab1db262SSlava Zakharin std::optional<mlir::Type> resultType, bool isElemental) { 293011b2af0SJean Perier fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 294011b2af0SJean Perier using PassBy = Fortran::lower::CallerInterface::PassEntityBy; 295011b2af0SJean Perier bool mustPopSymMap = false; 2968eee2360SjeanPerier if (caller.mustMapInterfaceSymbolsForResult()) { 297011b2af0SJean Perier symMap.pushScope(); 298011b2af0SJean Perier mustPopSymMap = true; 2998eee2360SjeanPerier Fortran::lower::mapCallInterfaceSymbolsForResult(converter, caller, symMap); 300011b2af0SJean Perier } 301011b2af0SJean Perier // If this is an indirect call, retrieve the function address. Also retrieve 302011b2af0SJean Perier // the result length if this is a character function (note that this length 303011b2af0SJean Perier // will be used only if there is no explicit length in the local interface). 304011b2af0SJean Perier mlir::Value funcPointer; 305011b2af0SJean Perier mlir::Value charFuncPointerLength; 306c373f581SjeanPerier if (const Fortran::evaluate::ProcedureDesignator *procDesignator = 307c373f581SjeanPerier caller.getIfIndirectCall()) { 308c373f581SjeanPerier if (mlir::Value passedArg = caller.getIfPassedArg()) { 309c373f581SjeanPerier // Procedure pointer component call with PASS argument. To avoid 310c373f581SjeanPerier // "double" lowering of the ComponentRef, semantics only place the 311c373f581SjeanPerier // ComponentRef in the ActualArguments, not in the ProcedureDesignator ( 312c373f581SjeanPerier // that is only the component symbol). 313c373f581SjeanPerier // Fetch the passed argument and addresses of its procedure pointer 314c373f581SjeanPerier // component. 315c373f581SjeanPerier funcPointer = Fortran::lower::derefPassProcPointerComponent( 316c373f581SjeanPerier loc, converter, *procDesignator, passedArg, symMap, stmtCtx); 317c373f581SjeanPerier } else { 318c373f581SjeanPerier Fortran::lower::SomeExpr expr{*procDesignator}; 319c373f581SjeanPerier fir::ExtendedValue loweredProc = 320c373f581SjeanPerier converter.genExprAddr(loc, expr, stmtCtx); 321c373f581SjeanPerier funcPointer = fir::getBase(loweredProc); 322c373f581SjeanPerier // Dummy procedure may have assumed length, in which case the result 323c373f581SjeanPerier // length was passed along the dummy procedure. 324c373f581SjeanPerier // This is not possible with procedure pointer components. 325c373f581SjeanPerier if (const fir::CharBoxValue *charBox = loweredProc.getCharBox()) 326c373f581SjeanPerier charFuncPointerLength = charBox->getLen(); 327c373f581SjeanPerier } 328011b2af0SJean Perier } 329011b2af0SJean Perier 330*cd7e6539SjeanPerier const bool isExprCall = 331*cd7e6539SjeanPerier converter.getLoweringOptions().getLowerToHighLevelFIR() && 332*cd7e6539SjeanPerier callSiteType.getNumResults() == 1 && 333*cd7e6539SjeanPerier llvm::isa<fir::SequenceType>(callSiteType.getResult(0)); 334*cd7e6539SjeanPerier 335011b2af0SJean Perier mlir::IndexType idxTy = builder.getIndexType(); 336011b2af0SJean Perier auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value { 337011b2af0SJean Perier mlir::Value convertExpr = builder.createConvert( 338011b2af0SJean Perier loc, idxTy, fir::getBase(converter.genExprValue(expr, stmtCtx))); 339011b2af0SJean Perier return fir::factory::genMaxWithZero(builder, loc, convertExpr); 340011b2af0SJean Perier }; 341011b2af0SJean Perier llvm::SmallVector<mlir::Value> resultLengths; 342*cd7e6539SjeanPerier mlir::Value arrayResultShape; 343*cd7e6539SjeanPerier hlfir::EvaluateInMemoryOp evaluateInMemory; 344c0921586SKazu Hirata auto allocatedResult = [&]() -> std::optional<fir::ExtendedValue> { 345011b2af0SJean Perier llvm::SmallVector<mlir::Value> extents; 346011b2af0SJean Perier llvm::SmallVector<mlir::Value> lengths; 347011b2af0SJean Perier if (!caller.callerAllocateResult()) 348011b2af0SJean Perier return {}; 349011b2af0SJean Perier mlir::Type type = caller.getResultStorageType(); 350fac349a1SChristian Sigg if (mlir::isa<fir::SequenceType>(type)) 3518eee2360SjeanPerier caller.walkResultExtents( 3528eee2360SjeanPerier [&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) { 3538eee2360SjeanPerier assert(!isAssumedSizeExtent && "result cannot be assumed-size"); 354011b2af0SJean Perier extents.emplace_back(lowerSpecExpr(e)); 355011b2af0SJean Perier }); 3568eee2360SjeanPerier caller.walkResultLengths( 3578eee2360SjeanPerier [&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) { 3588eee2360SjeanPerier assert(!isAssumedSizeExtent && "result cannot be assumed-size"); 359011b2af0SJean Perier lengths.emplace_back(lowerSpecExpr(e)); 360011b2af0SJean Perier }); 361011b2af0SJean Perier 362011b2af0SJean Perier // Result length parameters should not be provided to box storage 363011b2af0SJean Perier // allocation and save_results, but they are still useful information to 364011b2af0SJean Perier // keep in the ExtendedValue if non-deferred. 365fac349a1SChristian Sigg if (!mlir::isa<fir::BoxType>(type)) { 366011b2af0SJean Perier if (fir::isa_char(fir::unwrapSequenceType(type)) && lengths.empty()) { 367011b2af0SJean Perier // Calling an assumed length function. This is only possible if this 368011b2af0SJean Perier // is a call to a character dummy procedure. 369011b2af0SJean Perier if (!charFuncPointerLength) 370011b2af0SJean Perier fir::emitFatalError(loc, "failed to retrieve character function " 371011b2af0SJean Perier "length while calling it"); 372011b2af0SJean Perier lengths.push_back(charFuncPointerLength); 373011b2af0SJean Perier } 374011b2af0SJean Perier resultLengths = lengths; 375011b2af0SJean Perier } 376011b2af0SJean Perier 377*cd7e6539SjeanPerier if (!extents.empty()) 378*cd7e6539SjeanPerier arrayResultShape = builder.genShape(loc, extents); 379*cd7e6539SjeanPerier 380*cd7e6539SjeanPerier if (isExprCall) { 381*cd7e6539SjeanPerier mlir::Type exprType = hlfir::getExprType(type); 382*cd7e6539SjeanPerier evaluateInMemory = builder.create<hlfir::EvaluateInMemoryOp>( 383*cd7e6539SjeanPerier loc, exprType, arrayResultShape, resultLengths); 384*cd7e6539SjeanPerier builder.setInsertionPointToStart(&evaluateInMemory.getBody().front()); 385*cd7e6539SjeanPerier return toExtendedValue(loc, evaluateInMemory.getMemory(), extents, 386*cd7e6539SjeanPerier lengths); 387*cd7e6539SjeanPerier } 388*cd7e6539SjeanPerier 38994c024adSjeanPerier if ((!extents.empty() || !lengths.empty()) && !isElemental) { 39094c024adSjeanPerier // Note: in the elemental context, the alloca ownership inside the 39194c024adSjeanPerier // elemental region is implicit, and later pass in lowering (stack 39294c024adSjeanPerier // reclaim) fir.do_loop will be in charge of emitting any stack 39394c024adSjeanPerier // save/restore if needed. 394011b2af0SJean Perier auto *bldr = &converter.getFirOpBuilder(); 3955aaf384bSTom Eccles mlir::Value sp = bldr->genStackSave(loc); 3965aaf384bSTom Eccles stmtCtx.attachCleanup( 3975aaf384bSTom Eccles [bldr, loc, sp]() { bldr->genStackRestore(loc, sp); }); 398011b2af0SJean Perier } 399011b2af0SJean Perier mlir::Value temp = 400011b2af0SJean Perier builder.createTemporary(loc, type, ".result", extents, resultLengths); 401011b2af0SJean Perier return toExtendedValue(loc, temp, extents, lengths); 402011b2af0SJean Perier }(); 403011b2af0SJean Perier 404011b2af0SJean Perier if (mustPopSymMap) 405011b2af0SJean Perier symMap.popScope(); 406011b2af0SJean Perier 407*cd7e6539SjeanPerier // Place allocated result 408011b2af0SJean Perier if (allocatedResult) { 409011b2af0SJean Perier if (std::optional<Fortran::lower::CallInterface< 410011b2af0SJean Perier Fortran::lower::CallerInterface>::PassedEntity> 411011b2af0SJean Perier resultArg = caller.getPassedResult()) { 412011b2af0SJean Perier if (resultArg->passBy == PassBy::AddressAndLength) 413011b2af0SJean Perier caller.placeAddressAndLengthInput(*resultArg, 414011b2af0SJean Perier fir::getBase(*allocatedResult), 415011b2af0SJean Perier fir::getLen(*allocatedResult)); 416011b2af0SJean Perier else if (resultArg->passBy == PassBy::BaseAddress) 417011b2af0SJean Perier caller.placeInput(*resultArg, fir::getBase(*allocatedResult)); 418011b2af0SJean Perier else 419011b2af0SJean Perier fir::emitFatalError( 420011b2af0SJean Perier loc, "only expect character scalar result to be passed by ref"); 421011b2af0SJean Perier } 422011b2af0SJean Perier } 423011b2af0SJean Perier 424011b2af0SJean Perier // In older Fortran, procedure argument types are inferred. This may lead 425011b2af0SJean Perier // different view of what the function signature is in different locations. 426011b2af0SJean Perier // Casts are inserted as needed below to accommodate this. 427011b2af0SJean Perier 428011b2af0SJean Perier // The mlir::func::FuncOp type prevails, unless it has a different number of 429011b2af0SJean Perier // arguments which can happen in legal program if it was passed as a dummy 430011b2af0SJean Perier // procedure argument earlier with no further type information. 431011b2af0SJean Perier mlir::SymbolRefAttr funcSymbolAttr; 432011b2af0SJean Perier bool addHostAssociations = false; 433011b2af0SJean Perier if (!funcPointer) { 434011b2af0SJean Perier mlir::FunctionType funcOpType = caller.getFuncOp().getFunctionType(); 435011b2af0SJean Perier mlir::SymbolRefAttr symbolAttr = 436011b2af0SJean Perier builder.getSymbolRefAttr(caller.getMangledName()); 437011b2af0SJean Perier if (callSiteType.getNumResults() == funcOpType.getNumResults() && 438011b2af0SJean Perier callSiteType.getNumInputs() + 1 == funcOpType.getNumInputs() && 439011b2af0SJean Perier fir::anyFuncArgsHaveAttr(caller.getFuncOp(), 440011b2af0SJean Perier fir::getHostAssocAttrName())) { 441011b2af0SJean Perier // The number of arguments is off by one, and we're lowering a function 442011b2af0SJean Perier // with host associations. Modify call to include host associations 443011b2af0SJean Perier // argument by appending the value at the end of the operands. 444011b2af0SJean Perier assert(funcOpType.getInput(findHostAssocTuplePos(caller.getFuncOp())) == 445011b2af0SJean Perier converter.hostAssocTupleValue().getType()); 446011b2af0SJean Perier addHostAssociations = true; 447011b2af0SJean Perier } 44892e904b9SJean Perier // When this is not a call to an internal procedure (where there is a 44992e904b9SJean Perier // mismatch due to the extra argument, but the interface is otherwise 45092e904b9SJean Perier // explicit and safe), handle interface mismatch due to F77 implicit 45192e904b9SJean Perier // interface "abuse" with a function address cast if needed. 452011b2af0SJean Perier if (!addHostAssociations && 45392e904b9SJean Perier mustCastFuncOpToCopeWithImplicitInterfaceMismatch( 45492e904b9SJean Perier loc, converter, callSiteType, funcOpType)) 455011b2af0SJean Perier funcPointer = builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr); 45692e904b9SJean Perier else 457011b2af0SJean Perier funcSymbolAttr = symbolAttr; 458c5363678SSlava Zakharin 459c5363678SSlava Zakharin // Issue a warning if the procedure name conflicts with 460c5363678SSlava Zakharin // a runtime function name a call to which has been already 461c5363678SSlava Zakharin // lowered (implying that the FuncOp has been created). 462c5363678SSlava Zakharin // The behavior is undefined in this case. 463c5363678SSlava Zakharin if (caller.getFuncOp()->hasAttrOfType<mlir::UnitAttr>( 464c5363678SSlava Zakharin fir::FIROpsDialect::getFirRuntimeAttrName())) 465c5363678SSlava Zakharin LLVM_DEBUG(mlir::emitWarning( 466c5363678SSlava Zakharin loc, 467c5363678SSlava Zakharin llvm::Twine("function name '") + 468c5363678SSlava Zakharin llvm::Twine(symbolAttr.getLeafReference()) + 469c5363678SSlava Zakharin llvm::Twine("' conflicts with a runtime function name used by " 470c5363678SSlava Zakharin "Flang - this may lead to undefined behavior"))); 471011b2af0SJean Perier } 472011b2af0SJean Perier 473011b2af0SJean Perier mlir::FunctionType funcType = 474011b2af0SJean Perier funcPointer ? callSiteType : caller.getFuncOp().getFunctionType(); 475011b2af0SJean Perier llvm::SmallVector<mlir::Value> operands; 476011b2af0SJean Perier // First operand of indirect call is the function pointer. Cast it to 477011b2af0SJean Perier // required function type for the call to handle procedures that have a 478011b2af0SJean Perier // compatible interface in Fortran, but that have different signatures in 479011b2af0SJean Perier // FIR. 480011b2af0SJean Perier if (funcPointer) { 481011b2af0SJean Perier operands.push_back( 482fac349a1SChristian Sigg mlir::isa<fir::BoxProcType>(funcPointer.getType()) 483011b2af0SJean Perier ? builder.create<fir::BoxAddrOp>(loc, funcType, funcPointer) 484011b2af0SJean Perier : builder.createConvert(loc, funcType, funcPointer)); 485011b2af0SJean Perier } 486011b2af0SJean Perier 487011b2af0SJean Perier // Deal with potential mismatches in arguments types. Passing an array to a 488011b2af0SJean Perier // scalar argument should for instance be tolerated here. 489011b2af0SJean Perier bool callingImplicitInterface = caller.canBeCalledViaImplicitInterface(); 490011b2af0SJean Perier for (auto [fst, snd] : llvm::zip(caller.getInputs(), funcType.getInputs())) { 491011b2af0SJean Perier // When passing arguments to a procedure that can be called by implicit 492011b2af0SJean Perier // interface, allow any character actual arguments to be passed to dummy 493011b2af0SJean Perier // arguments of any type and vice versa. 494011b2af0SJean Perier mlir::Value cast; 495011b2af0SJean Perier auto *context = builder.getContext(); 496fac349a1SChristian Sigg if (mlir::isa<fir::BoxProcType>(snd) && 497fac349a1SChristian Sigg mlir::isa<mlir::FunctionType>(fst.getType())) { 4989a417395SKazu Hirata auto funcTy = 4999a417395SKazu Hirata mlir::FunctionType::get(context, std::nullopt, std::nullopt); 500011b2af0SJean Perier auto boxProcTy = builder.getBoxProcType(funcTy); 501011b2af0SJean Perier if (mlir::Value host = argumentHostAssocs(converter, fst)) { 502011b2af0SJean Perier cast = builder.create<fir::EmboxProcOp>( 503011b2af0SJean Perier loc, boxProcTy, llvm::ArrayRef<mlir::Value>{fst, host}); 504011b2af0SJean Perier } else { 505011b2af0SJean Perier cast = builder.create<fir::EmboxProcOp>(loc, boxProcTy, fst); 506011b2af0SJean Perier } 507011b2af0SJean Perier } else { 508011b2af0SJean Perier mlir::Type fromTy = fir::unwrapRefType(fst.getType()); 509011b2af0SJean Perier if (fir::isa_builtin_cptr_type(fromTy) && 510011b2af0SJean Perier Fortran::lower::isCPtrArgByValueType(snd)) { 511882e5f7bSSlava Zakharin cast = genRecordCPtrValueArg(builder, loc, fst, fromTy); 512dfb7d56aSjeanPerier } else if (fir::isa_derived(snd) && !fir::isa_derived(fst.getType())) { 513dfb7d56aSjeanPerier // TODO: remove this TODO once the old lowering is gone. 514011b2af0SJean Perier TODO(loc, "derived type argument passed by value"); 515011b2af0SJean Perier } else { 516a49f630cSjeanPerier // With the lowering to HLFIR, box arguments have already been built 517a49f630cSjeanPerier // according to the attributes, rank, bounds, and type they should have. 518a49f630cSjeanPerier // Do not attempt any reboxing here that could break this. 519a49f630cSjeanPerier bool legacyLowering = 520a49f630cSjeanPerier !converter.getLoweringOptions().getLowerToHighLevelFIR(); 521011b2af0SJean Perier cast = builder.convertWithSemantics(loc, snd, fst, 522a49f630cSjeanPerier callingImplicitInterface, 523a49f630cSjeanPerier /*allowRebox=*/legacyLowering); 524011b2af0SJean Perier } 525011b2af0SJean Perier } 526011b2af0SJean Perier operands.push_back(cast); 527011b2af0SJean Perier } 528011b2af0SJean Perier 529011b2af0SJean Perier // Add host associations as necessary. 530011b2af0SJean Perier if (addHostAssociations) 531011b2af0SJean Perier operands.push_back(converter.hostAssocTupleValue()); 532011b2af0SJean Perier 533011b2af0SJean Perier mlir::Value callResult; 534011b2af0SJean Perier unsigned callNumResults; 535a78359c2SjeanPerier fir::FortranProcedureFlagsEnumAttr procAttrs = 536a78359c2SjeanPerier caller.getProcedureAttrs(builder.getContext()); 5377106389bSValentin Clement (バレンタイン クレメン) 5387106389bSValentin Clement (バレンタイン クレメン) if (!caller.getCallDescription().chevrons().empty()) { 5397106389bSValentin Clement (バレンタイン クレメン) // A call to a CUDA kernel with the chevron syntax. 5407106389bSValentin Clement (バレンタイン クレメン) 5417106389bSValentin Clement (バレンタイン クレメン) mlir::Type i32Ty = builder.getI32Type(); 5427106389bSValentin Clement (バレンタイン クレメン) mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1); 5437106389bSValentin Clement (バレンタイン クレメン) 54465284be2SValentin Clement (バレンタイン クレメン) mlir::Value grid_x, grid_y, grid_z; 5457106389bSValentin Clement (バレンタイン クレメン) if (caller.getCallDescription().chevrons()[0].GetType()->category() == 5467106389bSValentin Clement (バレンタイン クレメン) Fortran::common::TypeCategory::Integer) { 5477106389bSValentin Clement (バレンタイン クレメン) // If grid is an integer, it is converted to dim3(grid,1,1). Since z is 5487106389bSValentin Clement (バレンタイン クレメン) // not used for the number of thread blocks, it is omitted in the op. 5497106389bSValentin Clement (バレンタイン クレメン) grid_x = builder.createConvert( 5507106389bSValentin Clement (バレンタイン クレメン) loc, i32Ty, 5517106389bSValentin Clement (バレンタイン クレメン) fir::getBase(converter.genExprValue( 5527106389bSValentin Clement (バレンタイン クレメン) caller.getCallDescription().chevrons()[0], stmtCtx))); 5537106389bSValentin Clement (バレンタイン クレメン) grid_y = one; 55465284be2SValentin Clement (バレンタイン クレメン) grid_z = one; 5557106389bSValentin Clement (バレンタイン クレメン) } else { 5567106389bSValentin Clement (バレンタイン クレメン) auto dim3Addr = converter.genExprAddr( 5577106389bSValentin Clement (バレンタイン クレメン) caller.getCallDescription().chevrons()[0], stmtCtx); 5587106389bSValentin Clement (バレンタイン クレメン) grid_x = readDim3Value(builder, loc, fir::getBase(dim3Addr), "x"); 5597106389bSValentin Clement (バレンタイン クレメン) grid_y = readDim3Value(builder, loc, fir::getBase(dim3Addr), "y"); 56065284be2SValentin Clement (バレンタイン クレメン) grid_z = readDim3Value(builder, loc, fir::getBase(dim3Addr), "z"); 5617106389bSValentin Clement (バレンタイン クレメン) } 5627106389bSValentin Clement (バレンタイン クレメン) 5637106389bSValentin Clement (バレンタイン クレメン) mlir::Value block_x, block_y, block_z; 5647106389bSValentin Clement (バレンタイン クレメン) if (caller.getCallDescription().chevrons()[1].GetType()->category() == 5657106389bSValentin Clement (バレンタイン クレメン) Fortran::common::TypeCategory::Integer) { 5667106389bSValentin Clement (バレンタイン クレメン) // If block is an integer, it is converted to dim3(block,1,1). 5677106389bSValentin Clement (バレンタイン クレメン) block_x = builder.createConvert( 5687106389bSValentin Clement (バレンタイン クレメン) loc, i32Ty, 5697106389bSValentin Clement (バレンタイン クレメン) fir::getBase(converter.genExprValue( 5707106389bSValentin Clement (バレンタイン クレメン) caller.getCallDescription().chevrons()[1], stmtCtx))); 5717106389bSValentin Clement (バレンタイン クレメン) block_y = one; 5727106389bSValentin Clement (バレンタイン クレメン) block_z = one; 5737106389bSValentin Clement (バレンタイン クレメン) } else { 5747106389bSValentin Clement (バレンタイン クレメン) auto dim3Addr = converter.genExprAddr( 5757106389bSValentin Clement (バレンタイン クレメン) caller.getCallDescription().chevrons()[1], stmtCtx); 5767106389bSValentin Clement (バレンタイン クレメン) block_x = readDim3Value(builder, loc, fir::getBase(dim3Addr), "x"); 5777106389bSValentin Clement (バレンタイン クレメン) block_y = readDim3Value(builder, loc, fir::getBase(dim3Addr), "y"); 5787106389bSValentin Clement (バレンタイン クレメン) block_z = readDim3Value(builder, loc, fir::getBase(dim3Addr), "z"); 5797106389bSValentin Clement (バレンタイン クレメン) } 5807106389bSValentin Clement (バレンタイン クレメン) 5817106389bSValentin Clement (バレンタイン クレメン) mlir::Value bytes; // bytes is optional. 5827106389bSValentin Clement (バレンタイン クレメン) if (caller.getCallDescription().chevrons().size() > 2) 5837106389bSValentin Clement (バレンタイン クレメン) bytes = builder.createConvert( 5847106389bSValentin Clement (バレンタイン クレメン) loc, i32Ty, 5857106389bSValentin Clement (バレンタイン クレメン) fir::getBase(converter.genExprValue( 5867106389bSValentin Clement (バレンタイン クレメン) caller.getCallDescription().chevrons()[2], stmtCtx))); 5877106389bSValentin Clement (バレンタイン クレメン) 5887106389bSValentin Clement (バレンタイン クレメン) mlir::Value stream; // stream is optional. 5897106389bSValentin Clement (バレンタイン クレメン) if (caller.getCallDescription().chevrons().size() > 3) 5907106389bSValentin Clement (バレンタイン クレメン) stream = builder.createConvert( 5917106389bSValentin Clement (バレンタイン クレメン) loc, i32Ty, 5927106389bSValentin Clement (バレンタイン クレメン) fir::getBase(converter.genExprValue( 5937106389bSValentin Clement (バレンタイン クレメン) caller.getCallDescription().chevrons()[3], stmtCtx))); 5947106389bSValentin Clement (バレンタイン クレメン) 59545daa4fdSValentin Clement (バレンタイン クレメン) builder.create<cuf::KernelLaunchOp>( 59665284be2SValentin Clement (バレンタイン クレメン) loc, funcType.getResults(), funcSymbolAttr, grid_x, grid_y, grid_z, 597b4d3c2caSIman Hosseini block_x, block_y, block_z, bytes, stream, operands); 5987106389bSValentin Clement (バレンタイン クレメン) callNumResults = 0; 5997106389bSValentin Clement (バレンタイン クレメン) } else if (caller.requireDispatchCall()) { 600011b2af0SJean Perier // Procedure call requiring a dynamic dispatch. Call is created with 601011b2af0SJean Perier // fir.dispatch. 602011b2af0SJean Perier 603011b2af0SJean Perier // Get the raw procedure name. The procedure name is not mangled in the 6047f7bbc73SPeter Klausler // binding table, but there can be a suffix to distinguish bindings of 6057f7bbc73SPeter Klausler // the same name (which happens only when PRIVATE bindings exist in 6067f7bbc73SPeter Klausler // ancestor types in other modules). 607011b2af0SJean Perier const auto &ultimateSymbol = 608011b2af0SJean Perier caller.getCallDescription().proc().GetSymbol()->GetUltimate(); 6097f7bbc73SPeter Klausler std::string procName = ultimateSymbol.name().ToString(); 6107f7bbc73SPeter Klausler if (const auto &binding{ 6117f7bbc73SPeter Klausler ultimateSymbol.get<Fortran::semantics::ProcBindingDetails>()}; 6127f7bbc73SPeter Klausler binding.numPrivatesNotOverridden() > 0) 6137f7bbc73SPeter Klausler procName += "."s + std::to_string(binding.numPrivatesNotOverridden()); 614011b2af0SJean Perier fir::DispatchOp dispatch; 615011b2af0SJean Perier if (std::optional<unsigned> passArg = caller.getPassArgIndex()) { 616011b2af0SJean Perier // PASS, PASS(arg-name) 617e8cc230eSjeanPerier // Note that caller.getInputs is used instead of operands to get the 618e8cc230eSjeanPerier // passed object because interface mismatch issues may have inserted a 619e8cc230eSjeanPerier // cast to the operand with a different declared type, which would break 620e8cc230eSjeanPerier // later type bound call resolution in the FIR to FIR pass. 621011b2af0SJean Perier dispatch = builder.create<fir::DispatchOp>( 622011b2af0SJean Perier loc, funcType.getResults(), builder.getStringAttr(procName), 623e8cc230eSjeanPerier caller.getInputs()[*passArg], operands, 624a78359c2SjeanPerier builder.getI32IntegerAttr(*passArg), procAttrs); 625011b2af0SJean Perier } else { 626011b2af0SJean Perier // NOPASS 627011b2af0SJean Perier const Fortran::evaluate::Component *component = 628011b2af0SJean Perier caller.getCallDescription().proc().GetComponent(); 629011b2af0SJean Perier assert(component && "expect component for type-bound procedure call."); 63067f9b5aeSValentin Clement (バレンタイン クレメン) 63167f9b5aeSValentin Clement (バレンタイン クレメン) fir::ExtendedValue dataRefValue = Fortran::lower::convertDataRefToValue( 63267f9b5aeSValentin Clement (バレンタイン クレメン) loc, converter, component->base(), symMap, stmtCtx); 63367f9b5aeSValentin Clement (バレンタイン クレメン) mlir::Value passObject = fir::getBase(dataRefValue); 63467f9b5aeSValentin Clement (バレンタイン クレメン) 635011b2af0SJean Perier if (fir::isa_ref_type(passObject.getType())) 63622c1c2d9SRenaud-K passObject = builder.create<fir::LoadOp>(loc, passObject); 637011b2af0SJean Perier dispatch = builder.create<fir::DispatchOp>( 638011b2af0SJean Perier loc, funcType.getResults(), builder.getStringAttr(procName), 639a78359c2SjeanPerier passObject, operands, nullptr, procAttrs); 640011b2af0SJean Perier } 641011b2af0SJean Perier callNumResults = dispatch.getNumResults(); 642d453af56SValentin Clement if (callNumResults != 0) 643d453af56SValentin Clement callResult = dispatch.getResult(0); 644011b2af0SJean Perier } else { 6452051a7bcSjeanPerier // Standard procedure call with fir.call. 6462051a7bcSjeanPerier auto call = builder.create<fir::CallOp>( 6472051a7bcSjeanPerier loc, funcType.getResults(), funcSymbolAttr, operands, procAttrs); 6489482af3dSValentin Clement (バレンタイン クレメン) 649011b2af0SJean Perier callNumResults = call.getNumResults(); 650d453af56SValentin Clement if (callNumResults != 0) 651d453af56SValentin Clement callResult = call.getResult(0); 652011b2af0SJean Perier } 653011b2af0SJean Perier 654*cd7e6539SjeanPerier std::optional<Fortran::evaluate::DynamicType> retTy = 655*cd7e6539SjeanPerier caller.getCallDescription().proc().GetType(); 656*cd7e6539SjeanPerier // With HLFIR lowering, isElemental must be set to true 657*cd7e6539SjeanPerier // if we are producing an elemental call. In this case, 658*cd7e6539SjeanPerier // the elemental results must not be destroyed, instead, 659*cd7e6539SjeanPerier // the resulting array result will be finalized/destroyed 660*cd7e6539SjeanPerier // as needed by hlfir.destroy. 661*cd7e6539SjeanPerier const bool mustFinalizeResult = 662*cd7e6539SjeanPerier !isElemental && callSiteType.getNumResults() > 0 && 663*cd7e6539SjeanPerier !fir::isPointerType(callSiteType.getResult(0)) && retTy.has_value() && 664*cd7e6539SjeanPerier (retTy->category() == Fortran::common::TypeCategory::Derived || 665*cd7e6539SjeanPerier retTy->IsPolymorphic() || retTy->IsUnlimitedPolymorphic()); 666*cd7e6539SjeanPerier 667a8234196SPeter Klausler if (caller.mustSaveResult()) { 668a8234196SPeter Klausler assert(allocatedResult.has_value()); 669011b2af0SJean Perier builder.create<fir::SaveResultOp>(loc, callResult, 67015a9a72eSFangrui Song fir::getBase(*allocatedResult), 671011b2af0SJean Perier arrayResultShape, resultLengths); 672a8234196SPeter Klausler } 673011b2af0SJean Perier 674*cd7e6539SjeanPerier if (evaluateInMemory) { 675*cd7e6539SjeanPerier builder.setInsertionPointAfter(evaluateInMemory); 676*cd7e6539SjeanPerier mlir::Value expr = evaluateInMemory.getResult(); 677*cd7e6539SjeanPerier fir::FirOpBuilder *bldr = &converter.getFirOpBuilder(); 678*cd7e6539SjeanPerier if (!isElemental) 679*cd7e6539SjeanPerier stmtCtx.attachCleanup([bldr, loc, expr, mustFinalizeResult]() { 680*cd7e6539SjeanPerier bldr->create<hlfir::DestroyOp>(loc, expr, 681*cd7e6539SjeanPerier /*finalize=*/mustFinalizeResult); 682*cd7e6539SjeanPerier }); 683*cd7e6539SjeanPerier return {LoweredResult{hlfir::EntityWithAttributes{expr}}, 684*cd7e6539SjeanPerier mustFinalizeResult}; 685*cd7e6539SjeanPerier } 686*cd7e6539SjeanPerier 687011b2af0SJean Perier if (allocatedResult) { 68835f5c8d7SSlava Zakharin // The result must be optionally destroyed (if it is of a derived type 68935f5c8d7SSlava Zakharin // that may need finalization or deallocation of the components). 69035f5c8d7SSlava Zakharin // For an allocatable result we have to free the memory allocated 69135f5c8d7SSlava Zakharin // for the top-level entity. Note that the Destroy calls below 69235f5c8d7SSlava Zakharin // do not deallocate the top-level entity. The two clean-ups 69335f5c8d7SSlava Zakharin // must be pushed in reverse order, so that the final order is: 69435f5c8d7SSlava Zakharin // Destroy(desc) 69535f5c8d7SSlava Zakharin // free(desc->base_addr) 69635f5c8d7SSlava Zakharin allocatedResult->match( 69735f5c8d7SSlava Zakharin [&](const fir::MutableBoxValue &box) { 69835f5c8d7SSlava Zakharin if (box.isAllocatable()) { 69935f5c8d7SSlava Zakharin // 9.7.3.2 point 4. Deallocate allocatable results. Note that 70035f5c8d7SSlava Zakharin // finalization was done independently by calling 70135f5c8d7SSlava Zakharin // genDerivedTypeDestroy above and is not triggered by this inline 70235f5c8d7SSlava Zakharin // deallocation. 70335f5c8d7SSlava Zakharin fir::FirOpBuilder *bldr = &converter.getFirOpBuilder(); 70435f5c8d7SSlava Zakharin stmtCtx.attachCleanup([bldr, loc, box]() { 70535f5c8d7SSlava Zakharin fir::factory::genFreememIfAllocated(*bldr, loc, box); 70635f5c8d7SSlava Zakharin }); 70735f5c8d7SSlava Zakharin } 70835f5c8d7SSlava Zakharin }, 70935f5c8d7SSlava Zakharin [](const auto &) {}); 71035f5c8d7SSlava Zakharin 711ad3a974bSValentin Clement // 7.5.6.3 point 5. Derived-type finalization for nonpointer function. 71267402fe5SjeanPerier bool resultIsFinalized = false; 713671eeeceSValentin Clement // Check if the derived-type is finalizable if it is a monomorphic 71497492fd1SValentin Clement // derived-type. 71597492fd1SValentin Clement // For polymorphic and unlimited polymorphic enities call the runtime 71697492fd1SValentin Clement // in any cases. 717*cd7e6539SjeanPerier if (mustFinalizeResult) { 71897492fd1SValentin Clement if (retTy->IsPolymorphic() || retTy->IsUnlimitedPolymorphic()) { 71997492fd1SValentin Clement auto *bldr = &converter.getFirOpBuilder(); 72097492fd1SValentin Clement stmtCtx.attachCleanup([bldr, loc, allocatedResult]() { 72197492fd1SValentin Clement fir::runtime::genDerivedTypeDestroy(*bldr, loc, 72297492fd1SValentin Clement fir::getBase(*allocatedResult)); 72397492fd1SValentin Clement }); 72467402fe5SjeanPerier resultIsFinalized = true; 72597492fd1SValentin Clement } else { 72697492fd1SValentin Clement const Fortran::semantics::DerivedTypeSpec &typeSpec = 72797492fd1SValentin Clement retTy->GetDerivedTypeSpec(); 72835f5c8d7SSlava Zakharin // If the result type may require finalization 72935f5c8d7SSlava Zakharin // or have allocatable components, we need to make sure 73035f5c8d7SSlava Zakharin // everything is properly finalized/deallocated. 73135f5c8d7SSlava Zakharin if (Fortran::semantics::MayRequireFinalization(typeSpec) || 73235f5c8d7SSlava Zakharin // We can use DerivedTypeDestroy even if finalization is not needed. 73335f5c8d7SSlava Zakharin hlfir::mayHaveAllocatableComponent(funcType.getResults()[0])) { 73497492fd1SValentin Clement auto *bldr = &converter.getFirOpBuilder(); 73597492fd1SValentin Clement stmtCtx.attachCleanup([bldr, loc, allocatedResult]() { 73697492fd1SValentin Clement mlir::Value box = bldr->createBox(loc, *allocatedResult); 73797492fd1SValentin Clement fir::runtime::genDerivedTypeDestroy(*bldr, loc, box); 73897492fd1SValentin Clement }); 73967402fe5SjeanPerier resultIsFinalized = true; 74097492fd1SValentin Clement } 74197492fd1SValentin Clement } 74297492fd1SValentin Clement } 743*cd7e6539SjeanPerier return {LoweredResult{*allocatedResult}, resultIsFinalized}; 744011b2af0SJean Perier } 745011b2af0SJean Perier 74667402fe5SjeanPerier // subroutine call 747011b2af0SJean Perier if (!resultType) 748*cd7e6539SjeanPerier return {LoweredResult{fir::ExtendedValue{mlir::Value{}}}, 749*cd7e6539SjeanPerier /*resultIsFinalized=*/false}; 75067402fe5SjeanPerier 751011b2af0SJean Perier // For now, Fortran return values are implemented with a single MLIR 752011b2af0SJean Perier // function return value. 753011b2af0SJean Perier assert(callNumResults == 1 && "Expected exactly one result in FUNCTION call"); 754011b2af0SJean Perier (void)callNumResults; 755011b2af0SJean Perier 756011b2af0SJean Perier // Call a BIND(C) function that return a char. 757011b2af0SJean Perier if (caller.characterize().IsBindC() && 758fac349a1SChristian Sigg mlir::isa<fir::CharacterType>(funcType.getResults()[0])) { 759011b2af0SJean Perier fir::CharacterType charTy = 760fac349a1SChristian Sigg mlir::dyn_cast<fir::CharacterType>(funcType.getResults()[0]); 761011b2af0SJean Perier mlir::Value len = builder.createIntegerConstant( 762011b2af0SJean Perier loc, builder.getCharacterLengthType(), charTy.getLen()); 763*cd7e6539SjeanPerier return { 764*cd7e6539SjeanPerier LoweredResult{fir::ExtendedValue{fir::CharBoxValue{callResult, len}}}, 765*cd7e6539SjeanPerier /*resultIsFinalized=*/false}; 766011b2af0SJean Perier } 767011b2af0SJean Perier 768*cd7e6539SjeanPerier return {LoweredResult{fir::ExtendedValue{callResult}}, 769*cd7e6539SjeanPerier /*resultIsFinalized=*/false}; 770011b2af0SJean Perier } 771e78e4a17SJean Perier 7728febe678SJean Perier static hlfir::EntityWithAttributes genStmtFunctionRef( 7738febe678SJean Perier mlir::Location loc, Fortran::lower::AbstractConverter &converter, 7748febe678SJean Perier Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx, 7758febe678SJean Perier const Fortran::evaluate::ProcedureRef &procRef) { 7768febe678SJean Perier const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol(); 7778febe678SJean Perier assert(symbol && "expected symbol in ProcedureRef of statement functions"); 7788febe678SJean Perier const auto &details = symbol->get<Fortran::semantics::SubprogramDetails>(); 7798febe678SJean Perier fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 7808febe678SJean Perier 7818febe678SJean Perier // Statement functions have their own scope, we just need to associate 7828febe678SJean Perier // the dummy symbols to argument expressions. There are no 7838febe678SJean Perier // optional/alternate return arguments. Statement functions cannot be 7848febe678SJean Perier // recursive (directly or indirectly) so it is safe to add dummy symbols to 7858febe678SJean Perier // the local map here. 7868febe678SJean Perier symMap.pushScope(); 7878febe678SJean Perier llvm::SmallVector<hlfir::AssociateOp> exprAssociations; 7888febe678SJean Perier for (auto [arg, bind] : llvm::zip(details.dummyArgs(), procRef.arguments())) { 7898febe678SJean Perier assert(arg && "alternate return in statement function"); 7908febe678SJean Perier assert(bind && "optional argument in statement function"); 7918febe678SJean Perier const auto *expr = bind->UnwrapExpr(); 7928febe678SJean Perier // TODO: assumed type in statement function, that surprisingly seems 7938febe678SJean Perier // allowed, probably because nobody thought of restricting this usage. 7948febe678SJean Perier // gfortran/ifort compiles this. 7958febe678SJean Perier assert(expr && "assumed type used as statement function argument"); 7968febe678SJean Perier // As per Fortran 2018 C1580, statement function arguments can only be 7978febe678SJean Perier // scalars. 7988febe678SJean Perier // The only care is to use the dummy character explicit length if any 7998febe678SJean Perier // instead of the actual argument length (that can be bigger). 8008febe678SJean Perier hlfir::EntityWithAttributes loweredArg = Fortran::lower::convertExprToHLFIR( 8018febe678SJean Perier loc, converter, *expr, symMap, stmtCtx); 8028febe678SJean Perier fir::FortranVariableOpInterface variableIface = loweredArg.getIfVariable(); 8038febe678SJean Perier if (!variableIface) { 8048febe678SJean Perier // So far only FortranVariableOpInterface can be mapped to symbols. 8058febe678SJean Perier // Create an hlfir.associate to create a variable from a potential 8068febe678SJean Perier // value argument. 8078febe678SJean Perier mlir::Type argType = converter.genType(*arg); 8088febe678SJean Perier auto associate = hlfir::genAssociateExpr( 8098febe678SJean Perier loc, builder, loweredArg, argType, toStringRef(arg->name())); 8108febe678SJean Perier exprAssociations.push_back(associate); 8118febe678SJean Perier variableIface = associate; 8128febe678SJean Perier } 8138febe678SJean Perier const Fortran::semantics::DeclTypeSpec *type = arg->GetType(); 8148febe678SJean Perier if (type && 8158febe678SJean Perier type->category() == Fortran::semantics::DeclTypeSpec::Character) { 8168febe678SJean Perier // Instantiate character as if it was a normal dummy argument so that the 8178febe678SJean Perier // statement function dummy character length is applied and dealt with 8188febe678SJean Perier // correctly. 8198febe678SJean Perier symMap.addSymbol(*arg, variableIface.getBase()); 8208febe678SJean Perier Fortran::lower::mapSymbolAttributes(converter, *arg, symMap, stmtCtx); 8218febe678SJean Perier } else { 8228febe678SJean Perier // No need to create an extra hlfir.declare otherwise for 8238febe678SJean Perier // numerical and logical scalar dummies. 8248febe678SJean Perier symMap.addVariableDefinition(*arg, variableIface); 8258febe678SJean Perier } 8268febe678SJean Perier } 8278febe678SJean Perier 8288febe678SJean Perier // Explicitly map statement function host associated symbols to their 8298febe678SJean Perier // parent scope lowered symbol box. 8308febe678SJean Perier for (const Fortran::semantics::SymbolRef &sym : 8318febe678SJean Perier Fortran::evaluate::CollectSymbols(*details.stmtFunction())) 8328febe678SJean Perier if (const auto *details = 8338febe678SJean Perier sym->detailsIf<Fortran::semantics::HostAssocDetails>()) 8348febe678SJean Perier converter.copySymbolBinding(details->symbol(), sym); 8358febe678SJean Perier 8368febe678SJean Perier hlfir::Entity result = Fortran::lower::convertExprToHLFIR( 8378febe678SJean Perier loc, converter, details.stmtFunction().value(), symMap, stmtCtx); 8388febe678SJean Perier symMap.popScope(); 8398febe678SJean Perier // The result must not be a variable. 8408febe678SJean Perier result = hlfir::loadTrivialScalar(loc, builder, result); 8418febe678SJean Perier if (result.isVariable()) 8428febe678SJean Perier result = hlfir::Entity{builder.create<hlfir::AsExprOp>(loc, result)}; 8438febe678SJean Perier for (auto associate : exprAssociations) 8448febe678SJean Perier builder.create<hlfir::EndAssociateOp>(loc, associate); 8458febe678SJean Perier return hlfir::EntityWithAttributes{result}; 8468febe678SJean Perier } 8478febe678SJean Perier 8483909b60aSJean Perier namespace { 8493909b60aSJean Perier // Structure to hold the information about the call and the lowering context. 8503909b60aSJean Perier // This structure is intended to help threading the information 8513909b60aSJean Perier // through the various lowering calls without having to pass every 8523909b60aSJean Perier // required structure one by one. 8533909b60aSJean Perier struct CallContext { 8543909b60aSJean Perier CallContext(const Fortran::evaluate::ProcedureRef &procRef, 8553909b60aSJean Perier std::optional<mlir::Type> resultType, mlir::Location loc, 8563909b60aSJean Perier Fortran::lower::AbstractConverter &converter, 8573909b60aSJean Perier Fortran::lower::SymMap &symMap, 8583909b60aSJean Perier Fortran::lower::StatementContext &stmtCtx) 8593909b60aSJean Perier : procRef{procRef}, converter{converter}, symMap{symMap}, 8603909b60aSJean Perier stmtCtx{stmtCtx}, resultType{resultType}, loc{loc} {} 8613909b60aSJean Perier 8623909b60aSJean Perier fir::FirOpBuilder &getBuilder() { return converter.getFirOpBuilder(); } 8633909b60aSJean Perier 864247bad16SJean Perier std::string getProcedureName() const { 865247bad16SJean Perier if (const Fortran::semantics::Symbol *sym = procRef.proc().GetSymbol()) 866247bad16SJean Perier return sym->GetUltimate().name().ToString(); 867247bad16SJean Perier return procRef.proc().GetName(); 868247bad16SJean Perier } 8696ed4a8b9SJean Perier 870e78e4a17SJean Perier /// Is this a call to an elemental procedure with at least one array argument? 8713909b60aSJean Perier bool isElementalProcWithArrayArgs() const { 872e78e4a17SJean Perier if (procRef.IsElemental()) 873e78e4a17SJean Perier for (const std::optional<Fortran::evaluate::ActualArgument> &arg : 874e78e4a17SJean Perier procRef.arguments()) 875e78e4a17SJean Perier if (arg && arg->Rank() != 0) 876e78e4a17SJean Perier return true; 877e78e4a17SJean Perier return false; 878e78e4a17SJean Perier } 879e78e4a17SJean Perier 8803909b60aSJean Perier /// Is this a statement function reference? 8813909b60aSJean Perier bool isStatementFunctionCall() const { 882e78e4a17SJean Perier if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol()) 883e78e4a17SJean Perier if (const auto *details = 884e78e4a17SJean Perier symbol->detailsIf<Fortran::semantics::SubprogramDetails>()) 885e78e4a17SJean Perier return details->stmtFunction().has_value(); 886e78e4a17SJean Perier return false; 887e78e4a17SJean Perier } 888e78e4a17SJean Perier 889a49f630cSjeanPerier /// Is this a call to a BIND(C) procedure? 890a49f630cSjeanPerier bool isBindcCall() const { 891a49f630cSjeanPerier if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol()) 892a49f630cSjeanPerier return Fortran::semantics::IsBindCProcedure(*symbol); 893a49f630cSjeanPerier return false; 894a49f630cSjeanPerier } 895a49f630cSjeanPerier 8963909b60aSJean Perier const Fortran::evaluate::ProcedureRef &procRef; 8973909b60aSJean Perier Fortran::lower::AbstractConverter &converter; 8983909b60aSJean Perier Fortran::lower::SymMap &symMap; 8993909b60aSJean Perier Fortran::lower::StatementContext &stmtCtx; 9003909b60aSJean Perier std::optional<mlir::Type> resultType; 9013909b60aSJean Perier mlir::Location loc; 9023909b60aSJean Perier }; 9031b74faddSTom Eccles 9041b74faddSTom Eccles using ExvAndCleanup = 9051b74faddSTom Eccles std::pair<fir::ExtendedValue, std::optional<hlfir::CleanupFunction>>; 9063909b60aSJean Perier } // namespace 9073909b60aSJean Perier 9083909b60aSJean Perier // Helper to transform a fir::ExtendedValue to an hlfir::EntityWithAttributes. 9093909b60aSJean Perier static hlfir::EntityWithAttributes 9103909b60aSJean Perier extendedValueToHlfirEntity(mlir::Location loc, fir::FirOpBuilder &builder, 9113909b60aSJean Perier const fir::ExtendedValue &exv, 9123909b60aSJean Perier llvm::StringRef name) { 9133909b60aSJean Perier mlir::Value firBase = fir::getBase(exv); 9149facbb69STom Eccles mlir::Type firBaseTy = firBase.getType(); 9159facbb69STom Eccles if (fir::isa_trivial(firBaseTy)) 9163909b60aSJean Perier return hlfir::EntityWithAttributes{firBase}; 917fac349a1SChristian Sigg if (auto charTy = mlir::dyn_cast<fir::CharacterType>(firBase.getType())) { 918219b997eSJean Perier // CHAR() intrinsic and BIND(C) procedures returning CHARACTER(1) 919219b997eSJean Perier // are lowered to a fir.char<kind,1> that is not in memory. 920219b997eSJean Perier // This tends to cause a lot of bugs because the rest of the 921219b997eSJean Perier // infrastructure is mostly tested with characters that are 922219b997eSJean Perier // in memory. 923219b997eSJean Perier // To avoid having to deal with this special case here and there, 924219b997eSJean Perier // place it in memory here. If this turns out to be suboptimal, 925219b997eSJean Perier // this could be fixed, but for now llvm opt -O1 is able to get 926219b997eSJean Perier // rid of the memory indirection in a = char(b), so there is 927219b997eSJean Perier // little incentive to increase the compiler complexity. 928219b997eSJean Perier hlfir::Entity storage{builder.createTemporary(loc, charTy)}; 929219b997eSJean Perier builder.create<fir::StoreOp>(loc, firBase, storage); 930219b997eSJean Perier auto asExpr = builder.create<hlfir::AsExprOp>( 931219b997eSJean Perier loc, storage, /*mustFree=*/builder.createBool(loc, false)); 932219b997eSJean Perier return hlfir::EntityWithAttributes{asExpr.getResult()}; 933219b997eSJean Perier } 9343909b60aSJean Perier return hlfir::genDeclare(loc, builder, exv, name, 9353909b60aSJean Perier fir::FortranVariableFlagsAttr{}); 9364203b062SJean Perier } 93787cd6f93SJean Perier namespace { 93887cd6f93SJean Perier /// Structure to hold the clean-up related to a dummy argument preparation 93987cd6f93SJean Perier /// that may have to be done after a call (copy-out or temporary deallocation). 94087cd6f93SJean Perier struct CallCleanUp { 94187cd6f93SJean Perier struct CopyIn { 94287cd6f93SJean Perier void genCleanUp(mlir::Location loc, fir::FirOpBuilder &builder) { 9439f44d5d9SjeanPerier builder.create<hlfir::CopyOutOp>(loc, tempBox, wasCopied, copyBackVar); 94487cd6f93SJean Perier } 9459f44d5d9SjeanPerier // address of the descriptor holding the temp if a temp was created. 9469f44d5d9SjeanPerier mlir::Value tempBox; 9479f44d5d9SjeanPerier // Boolean indicating if a copy was made or not. 94887cd6f93SJean Perier mlir::Value wasCopied; 94987cd6f93SJean Perier // copyBackVar may be null if copy back is not needed. 95087cd6f93SJean Perier mlir::Value copyBackVar; 95187cd6f93SJean Perier }; 95287cd6f93SJean Perier struct ExprAssociate { 95387cd6f93SJean Perier void genCleanUp(mlir::Location loc, fir::FirOpBuilder &builder) { 95487cd6f93SJean Perier builder.create<hlfir::EndAssociateOp>(loc, tempVar, mustFree); 95587cd6f93SJean Perier } 95687cd6f93SJean Perier mlir::Value tempVar; 95787cd6f93SJean Perier mlir::Value mustFree; 95887cd6f93SJean Perier }; 95987cd6f93SJean Perier void genCleanUp(mlir::Location loc, fir::FirOpBuilder &builder) { 96077d8cfb3SAlexander Shaposhnikov Fortran::common::visit([&](auto &c) { c.genCleanUp(loc, builder); }, 96177d8cfb3SAlexander Shaposhnikov cleanUp); 96287cd6f93SJean Perier } 96387cd6f93SJean Perier std::variant<CopyIn, ExprAssociate> cleanUp; 96487cd6f93SJean Perier }; 96587cd6f93SJean Perier 96687cd6f93SJean Perier /// Structure representing a prepared dummy argument. 96787cd6f93SJean Perier /// It holds the value to be passed in the call and any related 96887cd6f93SJean Perier /// clean-ups to be done after the call. 96987cd6f93SJean Perier struct PreparedDummyArgument { 9709f44d5d9SjeanPerier void pushCopyInCleanUp(mlir::Value tempBox, mlir::Value wasCopied, 97187cd6f93SJean Perier mlir::Value copyBackVar) { 972ab340f97SSlava Zakharin cleanups.emplace_back( 9739f44d5d9SjeanPerier CallCleanUp{CallCleanUp::CopyIn{tempBox, wasCopied, copyBackVar}}); 97487cd6f93SJean Perier } 975ab340f97SSlava Zakharin void pushExprAssociateCleanUp(mlir::Value tempVar, mlir::Value wasCopied) { 976ab340f97SSlava Zakharin cleanups.emplace_back( 977ab340f97SSlava Zakharin CallCleanUp{CallCleanUp::ExprAssociate{tempVar, wasCopied}}); 97887cd6f93SJean Perier } 979ab1db262SSlava Zakharin void pushExprAssociateCleanUp(hlfir::AssociateOp associate) { 980ab1db262SSlava Zakharin mlir::Value hlfirBase = associate.getBase(); 981ab1db262SSlava Zakharin mlir::Value firBase = associate.getFirBase(); 982ab1db262SSlava Zakharin cleanups.emplace_back(CallCleanUp{CallCleanUp::ExprAssociate{ 983ab1db262SSlava Zakharin hlfir::mayHaveAllocatableComponent(hlfirBase.getType()) ? hlfirBase 984ab1db262SSlava Zakharin : firBase, 985ab1db262SSlava Zakharin associate.getMustFreeStrorageFlag()}}); 986ab1db262SSlava Zakharin } 98787cd6f93SJean Perier 98887cd6f93SJean Perier mlir::Value dummy; 989ab340f97SSlava Zakharin // NOTE: the clean-ups are executed in reverse order. 990ab340f97SSlava Zakharin llvm::SmallVector<CallCleanUp, 2> cleanups; 99187cd6f93SJean Perier }; 99287cd6f93SJean Perier 99387cd6f93SJean Perier /// Structure to help conditionally preparing a dummy argument based 99487cd6f93SJean Perier /// on the actual argument presence. 99587cd6f93SJean Perier /// It helps "wrapping" the dummy and the clean-up information in 99687cd6f93SJean Perier /// an if (present) {...}: 99787cd6f93SJean Perier /// 99887cd6f93SJean Perier /// %conditionallyPrepared = fir.if (%present) { 99987cd6f93SJean Perier /// fir.result %preparedDummy 100087cd6f93SJean Perier /// } else { 100187cd6f93SJean Perier /// fir.result %absent 100287cd6f93SJean Perier /// } 100387cd6f93SJean Perier /// 100487cd6f93SJean Perier struct ConditionallyPreparedDummy { 100587cd6f93SJean Perier /// Create ConditionallyPreparedDummy from a preparedDummy that must 100687cd6f93SJean Perier /// be wrapped in a fir.if. 100787cd6f93SJean Perier ConditionallyPreparedDummy(PreparedDummyArgument &preparedDummy) { 100887cd6f93SJean Perier thenResultValues.push_back(preparedDummy.dummy); 1009ab340f97SSlava Zakharin for (const CallCleanUp &c : preparedDummy.cleanups) { 1010ab340f97SSlava Zakharin if (const auto *copyInCleanUp = 1011ab340f97SSlava Zakharin std::get_if<CallCleanUp::CopyIn>(&c.cleanUp)) { 101287cd6f93SJean Perier thenResultValues.push_back(copyInCleanUp->wasCopied); 101387cd6f93SJean Perier if (copyInCleanUp->copyBackVar) 101487cd6f93SJean Perier thenResultValues.push_back(copyInCleanUp->copyBackVar); 101587cd6f93SJean Perier } else { 1016ab340f97SSlava Zakharin const auto &exprAssociate = 1017ab340f97SSlava Zakharin std::get<CallCleanUp::ExprAssociate>(c.cleanUp); 101887cd6f93SJean Perier thenResultValues.push_back(exprAssociate.tempVar); 101987cd6f93SJean Perier thenResultValues.push_back(exprAssociate.mustFree); 102087cd6f93SJean Perier } 102187cd6f93SJean Perier } 102287cd6f93SJean Perier } 102387cd6f93SJean Perier 102487cd6f93SJean Perier /// Get the result types of the wrapping fir.if that must be created. 102587cd6f93SJean Perier llvm::SmallVector<mlir::Type> getIfResulTypes() const { 102687cd6f93SJean Perier llvm::SmallVector<mlir::Type> types; 102787cd6f93SJean Perier for (mlir::Value res : thenResultValues) 102887cd6f93SJean Perier types.push_back(res.getType()); 102987cd6f93SJean Perier return types; 103087cd6f93SJean Perier } 103187cd6f93SJean Perier 103287cd6f93SJean Perier /// Generate the "fir.result %preparedDummy" in the then branch of the 103387cd6f93SJean Perier /// wrapping fir.if. 103487cd6f93SJean Perier void genThenResult(mlir::Location loc, fir::FirOpBuilder &builder) const { 103587cd6f93SJean Perier builder.create<fir::ResultOp>(loc, thenResultValues); 103687cd6f93SJean Perier } 103787cd6f93SJean Perier 103887cd6f93SJean Perier /// Generate the "fir.result %absent" in the else branch of the 103987cd6f93SJean Perier /// wrapping fir.if. 104087cd6f93SJean Perier void genElseResult(mlir::Location loc, fir::FirOpBuilder &builder) const { 104187cd6f93SJean Perier llvm::SmallVector<mlir::Value> elseResultValues; 104287cd6f93SJean Perier mlir::Type i1Type = builder.getI1Type(); 104387cd6f93SJean Perier for (mlir::Value res : thenResultValues) { 104487cd6f93SJean Perier mlir::Type type = res.getType(); 104587cd6f93SJean Perier if (type == i1Type) 104687cd6f93SJean Perier elseResultValues.push_back(builder.createBool(loc, false)); 104787cd6f93SJean Perier else 104866ec3263SLeandro Lupori elseResultValues.push_back(builder.genAbsentOp(loc, type)); 104987cd6f93SJean Perier } 105087cd6f93SJean Perier builder.create<fir::ResultOp>(loc, elseResultValues); 105187cd6f93SJean Perier } 105287cd6f93SJean Perier 105387cd6f93SJean Perier /// Once the fir.if has been created, get the resulting %conditionallyPrepared 105487cd6f93SJean Perier /// dummy argument. 105587cd6f93SJean Perier PreparedDummyArgument 105687cd6f93SJean Perier getPreparedDummy(fir::IfOp ifOp, 105787cd6f93SJean Perier const PreparedDummyArgument &unconditionalDummy) { 105887cd6f93SJean Perier PreparedDummyArgument preparedDummy; 105987cd6f93SJean Perier preparedDummy.dummy = ifOp.getResults()[0]; 1060ab340f97SSlava Zakharin for (const CallCleanUp &c : unconditionalDummy.cleanups) { 1061ab340f97SSlava Zakharin if (const auto *copyInCleanUp = 1062ab340f97SSlava Zakharin std::get_if<CallCleanUp::CopyIn>(&c.cleanUp)) { 106387cd6f93SJean Perier mlir::Value copyBackVar; 106487cd6f93SJean Perier if (copyInCleanUp->copyBackVar) 106587cd6f93SJean Perier copyBackVar = ifOp.getResults().back(); 10669f44d5d9SjeanPerier // tempBox is an hlfir.copy_in argument created outside of the 10679f44d5d9SjeanPerier // fir.if region. It needs not to be threaded as a fir.if result. 10689f44d5d9SjeanPerier preparedDummy.pushCopyInCleanUp(copyInCleanUp->tempBox, 10699f44d5d9SjeanPerier ifOp.getResults()[1], copyBackVar); 107087cd6f93SJean Perier } else { 1071ab340f97SSlava Zakharin preparedDummy.pushExprAssociateCleanUp(ifOp.getResults()[1], 107287cd6f93SJean Perier ifOp.getResults()[2]); 107387cd6f93SJean Perier } 107487cd6f93SJean Perier } 107587cd6f93SJean Perier return preparedDummy; 107687cd6f93SJean Perier } 107787cd6f93SJean Perier 107887cd6f93SJean Perier llvm::SmallVector<mlir::Value> thenResultValues; 107987cd6f93SJean Perier }; 108087cd6f93SJean Perier } // namespace 108187cd6f93SJean Perier 108292e904b9SJean Perier /// Fix-up the fact that it is supported to pass a character procedure 108392e904b9SJean Perier /// designator to a non character procedure dummy procedure and vice-versa, even 108492e904b9SJean Perier /// in case of explicit interface. Uglier cases where an object is passed as 108592e904b9SJean Perier /// procedure designator or vice versa are handled only for implicit interfaces 108692e904b9SJean Perier /// (refused by semantics with explicit interface), and handled with a funcOp 108792e904b9SJean Perier /// cast like other implicit interface mismatches. 108892e904b9SJean Perier static hlfir::Entity fixProcedureDummyMismatch(mlir::Location loc, 108992e904b9SJean Perier fir::FirOpBuilder &builder, 109092e904b9SJean Perier hlfir::Entity actual, 109192e904b9SJean Perier mlir::Type dummyType) { 1092fac349a1SChristian Sigg if (mlir::isa<fir::BoxProcType>(actual.getType()) && 109392e904b9SJean Perier fir::isCharacterProcedureTuple(dummyType)) { 109492e904b9SJean Perier mlir::Value length = 109592e904b9SJean Perier builder.create<fir::UndefOp>(loc, builder.getCharacterLengthType()); 109692e904b9SJean Perier mlir::Value tuple = fir::factory::createCharacterProcedureTuple( 109792e904b9SJean Perier builder, loc, dummyType, actual, length); 109892e904b9SJean Perier return hlfir::Entity{tuple}; 109992e904b9SJean Perier } 110092e904b9SJean Perier assert(fir::isCharacterProcedureTuple(actual.getType()) && 1101fac349a1SChristian Sigg mlir::isa<fir::BoxProcType>(dummyType) && 110292e904b9SJean Perier "unsupported dummy procedure mismatch with the actual argument"); 110392e904b9SJean Perier mlir::Value boxProc = fir::factory::extractCharacterProcedureTuple( 110492e904b9SJean Perier builder, loc, actual, /*openBoxProc=*/false) 110592e904b9SJean Perier .first; 110692e904b9SJean Perier return hlfir::Entity{boxProc}; 110792e904b9SJean Perier } 110892e904b9SJean Perier 1109a49f630cSjeanPerier mlir::Value static getZeroLowerBounds(mlir::Location loc, 1110a49f630cSjeanPerier fir::FirOpBuilder &builder, 1111a49f630cSjeanPerier hlfir::Entity entity) { 1112453a0e4cSjeanPerier assert(!entity.isAssumedRank() && 1113453a0e4cSjeanPerier "assumed-rank must use fir.rebox_assumed_rank"); 1114a49f630cSjeanPerier if (entity.getRank() < 1) 1115a49f630cSjeanPerier return {}; 1116a49f630cSjeanPerier mlir::Value zero = 1117a49f630cSjeanPerier builder.createIntegerConstant(loc, builder.getIndexType(), 0); 1118a49f630cSjeanPerier llvm::SmallVector<mlir::Value> lowerBounds(entity.getRank(), zero); 1119a49f630cSjeanPerier return builder.genShift(loc, lowerBounds); 1120a49f630cSjeanPerier } 1121a49f630cSjeanPerier 11222984699aSjeanPerier static bool 11232984699aSjeanPerier isSimplyContiguous(const Fortran::evaluate::ActualArgument &arg, 11242984699aSjeanPerier Fortran::evaluate::FoldingContext &foldingContext) { 11252984699aSjeanPerier if (const auto *expr = arg.UnwrapExpr()) 11262984699aSjeanPerier return Fortran::evaluate::IsSimplyContiguous(*expr, foldingContext); 11272984699aSjeanPerier const Fortran::semantics::Symbol *sym = arg.GetAssumedTypeDummy(); 11282984699aSjeanPerier assert(sym && 11292984699aSjeanPerier "expect ActualArguments to be expression or assumed-type symbols"); 11302984699aSjeanPerier return sym->Rank() == 0 || 11312984699aSjeanPerier Fortran::evaluate::IsSimplyContiguous(*sym, foldingContext); 11322984699aSjeanPerier } 11332984699aSjeanPerier 113487cd6f93SJean Perier /// When dummy is not ALLOCATABLE, POINTER and is not passed in register, 113587cd6f93SJean Perier /// prepare the actual argument according to the interface. Do as needed: 113687cd6f93SJean Perier /// - address element if this is an array argument in an elemental call. 113787cd6f93SJean Perier /// - set dynamic type to the dummy type if the dummy is not polymorphic. 113887cd6f93SJean Perier /// - copy-in into contiguous variable if the dummy must be contiguous 113987cd6f93SJean Perier /// - copy into a temporary if the dummy has the VALUE attribute. 114087cd6f93SJean Perier /// - package the prepared dummy as required (fir.box, fir.class, 114187cd6f93SJean Perier /// fir.box_char...). 114287cd6f93SJean Perier /// This function should only be called with an actual that is present. 114387cd6f93SJean Perier /// The optional aspects must be handled by this function user. 114487cd6f93SJean Perier static PreparedDummyArgument preparePresentUserCallActualArgument( 114587cd6f93SJean Perier mlir::Location loc, fir::FirOpBuilder &builder, 1146d2d21301STom Eccles const Fortran::lower::PreparedActualArgument &preparedActual, 1147d2d21301STom Eccles mlir::Type dummyType, 114887cd6f93SJean Perier const Fortran::lower::CallerInterface::PassedEntity &arg, 11492984699aSjeanPerier CallContext &callContext) { 11506db45cc4SSlava Zakharin 11516db45cc4SSlava Zakharin Fortran::evaluate::FoldingContext &foldingContext = 1152a49f630cSjeanPerier callContext.converter.getFoldingContext(); 115387cd6f93SJean Perier 115487cd6f93SJean Perier // Step 1: get the actual argument, which includes addressing the 115587cd6f93SJean Perier // element if this is an array in an elemental call. 115687cd6f93SJean Perier hlfir::Entity actual = preparedActual.getActual(loc, builder); 115787cd6f93SJean Perier 115809b4649eSjeanPerier // Handle procedure arguments (procedure pointers should go through 115909b4649eSjeanPerier // prepareProcedurePointerActualArgument). 1160af09219eSDaniel Chen if (hlfir::isFortranProcedureValue(dummyType)) { 1161a4ac0994SDaniel Chen // Procedure pointer or function returns procedure pointer actual to 1162a4ac0994SDaniel Chen // procedure dummy. 116309b4649eSjeanPerier if (actual.isProcedurePointer()) { 1164af09219eSDaniel Chen actual = hlfir::derefPointersAndAllocatables(loc, builder, actual); 1165af09219eSDaniel Chen return PreparedDummyArgument{actual, /*cleanups=*/{}}; 1166af09219eSDaniel Chen } 1167af09219eSDaniel Chen // Procedure actual to procedure dummy. 116809b4649eSjeanPerier assert(actual.isProcedure()); 1169cedfd272SJean Perier // Do nothing if this is a procedure argument. It is already a 1170cedfd272SJean Perier // fir.boxproc/fir.tuple<fir.boxproc, len> as it should. 1171fac349a1SChristian Sigg if (!mlir::isa<fir::BoxProcType>(actual.getType()) && 1172a4ac0994SDaniel Chen actual.getType() != dummyType) 1173a4ac0994SDaniel Chen // The actual argument may be a procedure that returns character (a 1174a4ac0994SDaniel Chen // fir.tuple<fir.boxproc, len>) while the dummy is not. Extract the tuple 1175a4ac0994SDaniel Chen // in that case. 117692e904b9SJean Perier actual = fixProcedureDummyMismatch(loc, builder, actual, dummyType); 1177ab340f97SSlava Zakharin return PreparedDummyArgument{actual, /*cleanups=*/{}}; 117892e904b9SJean Perier } 1179cedfd272SJean Perier 1180a49f630cSjeanPerier const bool ignoreTKRtype = arg.testTKR(Fortran::common::IgnoreTKR::Type); 118187cd6f93SJean Perier const bool passingPolymorphicToNonPolymorphic = 1182a49f630cSjeanPerier actual.isPolymorphic() && !fir::isPolymorphicType(dummyType) && 1183a49f630cSjeanPerier !ignoreTKRtype; 118487cd6f93SJean Perier 118587cd6f93SJean Perier // When passing a CLASS(T) to TYPE(T), only the "T" part must be 118687cd6f93SJean Perier // passed. Unless the entity is a scalar passed by raw address, a 118787cd6f93SJean Perier // new descriptor must be made using the dummy argument type as 118887cd6f93SJean Perier // dynamic type. This must be done before any copy/copy-in because the 118987cd6f93SJean Perier // dynamic type matters to determine the contiguity. 119087cd6f93SJean Perier const bool mustSetDynamicTypeToDummyType = 119187cd6f93SJean Perier passingPolymorphicToNonPolymorphic && 1192fac349a1SChristian Sigg (actual.isArray() || mlir::isa<fir::BaseBoxType>(dummyType)); 119387cd6f93SJean Perier 119487cd6f93SJean Perier // The simple contiguity of the actual is "lost" when passing a polymorphic 119587cd6f93SJean Perier // to a non polymorphic entity because the dummy dynamic type matters for 119687cd6f93SJean Perier // the contiguity. 119787cd6f93SJean Perier const bool mustDoCopyInOut = 119887cd6f93SJean Perier actual.isArray() && arg.mustBeMadeContiguous() && 119987cd6f93SJean Perier (passingPolymorphicToNonPolymorphic || 12002984699aSjeanPerier !isSimplyContiguous(*arg.entity, foldingContext)); 120187cd6f93SJean Perier 1202a49f630cSjeanPerier const bool actualIsAssumedRank = actual.isAssumedRank(); 1203a49f630cSjeanPerier // Create dummy type with actual argument rank when the dummy is an assumed 1204a49f630cSjeanPerier // rank. That way, all the operation to create dummy descriptors are ranked if 1205a49f630cSjeanPerier // the actual argument is ranked, which allows simple code generation. 12068eee2360SjeanPerier // Also do the same when the dummy is a sequence associated descriptor 12078eee2360SjeanPerier // because the actual shape/rank may mismatch with the dummy, and the dummy 12088eee2360SjeanPerier // may be an assumed-size array, so any descriptor manipulation should use the 12098eee2360SjeanPerier // actual argument shape information. A descriptor with the dummy shape 12108eee2360SjeanPerier // information will be created later when all actual arguments are ready. 1211a49f630cSjeanPerier mlir::Type dummyTypeWithActualRank = dummyType; 1212d6173167SDaniel Chen if (auto baseBoxDummy = mlir::dyn_cast<fir::BaseBoxType>(dummyType)) { 1213a49f630cSjeanPerier if (baseBoxDummy.isAssumedRank() || 12148eee2360SjeanPerier arg.testTKR(Fortran::common::IgnoreTKR::Rank) || 1215d6173167SDaniel Chen arg.isSequenceAssociatedDescriptor()) { 1216d6173167SDaniel Chen mlir::Type actualTy = 1217d6173167SDaniel Chen hlfir::getFortranElementOrSequenceType(actual.getType()); 1218d6173167SDaniel Chen dummyTypeWithActualRank = baseBoxDummy.getBoxTypeWithNewShape(actualTy); 1219d6173167SDaniel Chen } 1220d6173167SDaniel Chen } 1221a49f630cSjeanPerier // Preserve the actual type in the argument preparation in case IgnoreTKR(t) 1222a49f630cSjeanPerier // is set (descriptors must be created with the actual type in this case, and 1223a49f630cSjeanPerier // copy-in/copy-out should be driven by the contiguity with regard to the 1224a49f630cSjeanPerier // actual type). 1225e6618aaeSjeanPerier if (ignoreTKRtype) { 1226e6618aaeSjeanPerier if (auto boxCharType = 1227e6618aaeSjeanPerier mlir::dyn_cast<fir::BoxCharType>(dummyTypeWithActualRank)) { 1228e6618aaeSjeanPerier auto maybeActualCharType = 1229e6618aaeSjeanPerier mlir::dyn_cast<fir::CharacterType>(actual.getFortranElementType()); 1230e6618aaeSjeanPerier if (!maybeActualCharType || 1231e6618aaeSjeanPerier maybeActualCharType.getFKind() != boxCharType.getKind()) { 1232e6618aaeSjeanPerier // When passing to a fir.boxchar with ignore(tk), prepare the argument 1233e6618aaeSjeanPerier // as if only the raw address must be passed. 1234e6618aaeSjeanPerier dummyTypeWithActualRank = 1235e6618aaeSjeanPerier fir::ReferenceType::get(actual.getElementOrSequenceType()); 1236e6618aaeSjeanPerier } 1237e6618aaeSjeanPerier // Otherwise, the actual is already a character with the same kind as the 1238e6618aaeSjeanPerier // dummy and can be passed normally. 1239e6618aaeSjeanPerier } else { 1240a49f630cSjeanPerier dummyTypeWithActualRank = fir::changeElementType( 1241a49f630cSjeanPerier dummyTypeWithActualRank, actual.getFortranElementType(), 1242a49f630cSjeanPerier actual.isPolymorphic()); 1243e6618aaeSjeanPerier } 1244e6618aaeSjeanPerier } 1245a49f630cSjeanPerier 12469f44d5d9SjeanPerier PreparedDummyArgument preparedDummy; 12479f44d5d9SjeanPerier 12489f44d5d9SjeanPerier // Helpers to generate hlfir.copy_in operation and register the related 12499f44d5d9SjeanPerier // hlfir.copy_out creation. 12509f44d5d9SjeanPerier auto genCopyIn = [&](hlfir::Entity var, bool doCopyOut) -> hlfir::Entity { 12519f44d5d9SjeanPerier auto baseBoxTy = mlir::dyn_cast<fir::BaseBoxType>(var.getType()); 12529f44d5d9SjeanPerier assert(baseBoxTy && "expect non simply contiguous variables to be boxes"); 12539f44d5d9SjeanPerier // Create allocatable descriptor for the potential temporary. 12549f44d5d9SjeanPerier mlir::Type tempBoxType = baseBoxTy.getBoxTypeWithNewAttr( 12559f44d5d9SjeanPerier fir::BaseBoxType::Attribute::Allocatable); 12569f44d5d9SjeanPerier mlir::Value tempBox = builder.createTemporary(loc, tempBoxType); 12579f44d5d9SjeanPerier auto copyIn = builder.create<hlfir::CopyInOp>( 12589f44d5d9SjeanPerier loc, var, tempBox, /*var_is_present=*/mlir::Value{}); 12599f44d5d9SjeanPerier // Register the copy-out after the call. 12609f44d5d9SjeanPerier preparedDummy.pushCopyInCleanUp(copyIn.getTempBox(), copyIn.getWasCopied(), 12619f44d5d9SjeanPerier doCopyOut ? copyIn.getVar() 12629f44d5d9SjeanPerier : mlir::Value{}); 12639f44d5d9SjeanPerier return hlfir::Entity{copyIn.getCopiedIn()}; 12649f44d5d9SjeanPerier }; 12659f44d5d9SjeanPerier 1266462d0842SjeanPerier auto genSetDynamicTypeToDummyType = [&](hlfir::Entity var) -> hlfir::Entity { 1267462d0842SjeanPerier fir::BaseBoxType boxType = fir::BoxType::get( 1268462d0842SjeanPerier hlfir::getFortranElementOrSequenceType(dummyTypeWithActualRank)); 1269462d0842SjeanPerier if (actualIsAssumedRank) 1270462d0842SjeanPerier return hlfir::Entity{builder.create<fir::ReboxAssumedRankOp>( 1271462d0842SjeanPerier loc, boxType, var, fir::LowerBoundModifierAttribute::SetToOnes)}; 1272462d0842SjeanPerier // Use actual shape when creating descriptor with dummy type, the dummy 1273462d0842SjeanPerier // shape may be unknown in case of sequence association. 1274462d0842SjeanPerier mlir::Type actualTy = 1275462d0842SjeanPerier hlfir::getFortranElementOrSequenceType(actual.getType()); 1276462d0842SjeanPerier boxType = boxType.getBoxTypeWithNewShape(actualTy); 1277462d0842SjeanPerier return hlfir::Entity{builder.create<fir::ReboxOp>(loc, boxType, var, 1278462d0842SjeanPerier /*shape=*/mlir::Value{}, 1279462d0842SjeanPerier /*slice=*/mlir::Value{})}; 1280462d0842SjeanPerier }; 1281462d0842SjeanPerier 128287cd6f93SJean Perier // Step 2: prepare the storage for the dummy arguments, ensuring that it 128387cd6f93SJean Perier // matches the dummy requirements (e.g., must be contiguous or must be 128487cd6f93SJean Perier // a temporary). 128587cd6f93SJean Perier hlfir::Entity entity = 128687cd6f93SJean Perier hlfir::derefPointersAndAllocatables(loc, builder, actual); 128787cd6f93SJean Perier if (entity.isVariable()) { 1288462d0842SjeanPerier // Set dynamic type if needed before any copy-in or copy so that the dummy 1289462d0842SjeanPerier // is contiguous according to the dummy type. 1290462d0842SjeanPerier if (mustSetDynamicTypeToDummyType) 1291462d0842SjeanPerier entity = genSetDynamicTypeToDummyType(entity); 12920daa80e8SSlava Zakharin if (arg.hasValueAttribute() || 12930daa80e8SSlava Zakharin // Constant expressions might be lowered as variables with 12940daa80e8SSlava Zakharin // 'parameter' attribute. Even though the constant expressions 12950daa80e8SSlava Zakharin // are not definable and explicit assignments to them are not 12960daa80e8SSlava Zakharin // possible, we have to create a temporary copies when we pass 12970daa80e8SSlava Zakharin // them down the call stack. 12980daa80e8SSlava Zakharin entity.isParameter()) { 129987cd6f93SJean Perier // Make a copy in a temporary. 130087cd6f93SJean Perier auto copy = builder.create<hlfir::AsExprOp>(loc, entity); 1301d07f23e0SJean Perier mlir::Type storageType = entity.getType(); 13020ccef6a7SMats Petersson mlir::NamedAttribute byRefAttr = fir::getAdaptToByRefAttr(builder); 130387cd6f93SJean Perier hlfir::AssociateOp associate = hlfir::genAssociateExpr( 13040ccef6a7SMats Petersson loc, builder, hlfir::Entity{copy}, storageType, "", byRefAttr); 130587cd6f93SJean Perier entity = hlfir::Entity{associate.getBase()}; 130687cd6f93SJean Perier // Register the temporary destruction after the call. 1307ab1db262SSlava Zakharin preparedDummy.pushExprAssociateCleanUp(associate); 130887cd6f93SJean Perier } else if (mustDoCopyInOut) { 130987cd6f93SJean Perier // Copy-in non contiguous variables. 1310da60b9e7SSlava Zakharin // TODO: for non-finalizable monomorphic derived type actual 1311da60b9e7SSlava Zakharin // arguments associated with INTENT(OUT) dummy arguments 1312da60b9e7SSlava Zakharin // we may avoid doing the copy and only allocate the temporary. 1313da60b9e7SSlava Zakharin // The codegen would do a "mold" allocation instead of "sourced" 1314da60b9e7SSlava Zakharin // allocation for the temp in this case. We can communicate 1315da60b9e7SSlava Zakharin // this to the codegen via some CopyInOp flag. 1316da60b9e7SSlava Zakharin // This is a performance concern. 13179f44d5d9SjeanPerier entity = genCopyIn(entity, arg.mayBeModifiedByCall()); 131887cd6f93SJean Perier } 131987cd6f93SJean Perier } else { 13202984699aSjeanPerier const Fortran::lower::SomeExpr *expr = arg.entity->UnwrapExpr(); 13212984699aSjeanPerier assert(expr && "expression actual argument cannot be an assumed type"); 132287cd6f93SJean Perier // The actual is an expression value, place it into a temporary 132387cd6f93SJean Perier // and register the temporary destruction after the call. 13242984699aSjeanPerier mlir::Type storageType = callContext.converter.genType(*expr); 13250ccef6a7SMats Petersson mlir::NamedAttribute byRefAttr = fir::getAdaptToByRefAttr(builder); 132687cd6f93SJean Perier hlfir::AssociateOp associate = hlfir::genAssociateExpr( 13270ccef6a7SMats Petersson loc, builder, entity, storageType, "", byRefAttr); 132887cd6f93SJean Perier entity = hlfir::Entity{associate.getBase()}; 1329ab1db262SSlava Zakharin preparedDummy.pushExprAssociateCleanUp(associate); 1330462d0842SjeanPerier // Rebox the actual argument to the dummy argument's type, and make sure 1331462d0842SjeanPerier // that we pass a contiguous entity (i.e. make copy-in, if needed). 1332ab340f97SSlava Zakharin // 1333462d0842SjeanPerier // TODO: this can probably be optimized by associating the expression with 1334462d0842SjeanPerier // properly typed temporary, but this needs either a new operation or 1335462d0842SjeanPerier // making the hlfir.associate more complex. 1336462d0842SjeanPerier if (mustSetDynamicTypeToDummyType) { 1337462d0842SjeanPerier entity = genSetDynamicTypeToDummyType(entity); 13389f44d5d9SjeanPerier entity = genCopyIn(entity, /*doCopyOut=*/false); 1339ab340f97SSlava Zakharin } 134087cd6f93SJean Perier } 134187cd6f93SJean Perier 134287cd6f93SJean Perier // Step 3: now that the dummy argument storage has been prepared, package 134387cd6f93SJean Perier // it according to the interface. 134487cd6f93SJean Perier mlir::Value addr; 1345fac349a1SChristian Sigg if (mlir::isa<fir::BoxCharType>(dummyTypeWithActualRank)) { 134687cd6f93SJean Perier addr = hlfir::genVariableBoxChar(loc, builder, entity); 1347fac349a1SChristian Sigg } else if (mlir::isa<fir::BaseBoxType>(dummyTypeWithActualRank)) { 134887cd6f93SJean Perier entity = hlfir::genVariableBox(loc, builder, entity); 134987cd6f93SJean Perier // Ensures the box has the right attributes and that it holds an 135087cd6f93SJean Perier // addendum if needed. 1351fac349a1SChristian Sigg fir::BaseBoxType actualBoxType = 1352fac349a1SChristian Sigg mlir::cast<fir::BaseBoxType>(entity.getType()); 1353f5dbee00SSlava Zakharin mlir::Type boxEleType = actualBoxType.getEleTy(); 135487cd6f93SJean Perier // For now, assume it is not OK to pass the allocatable/pointer 135587cd6f93SJean Perier // descriptor to a non pointer/allocatable dummy. That is a strict 135687cd6f93SJean Perier // interpretation of 18.3.6 point 4 that stipulates the descriptor 135787cd6f93SJean Perier // has the dummy attributes in BIND(C) contexts. 135887cd6f93SJean Perier const bool actualBoxHasAllocatableOrPointerFlag = 135987cd6f93SJean Perier fir::isa_ref_type(boxEleType); 1360a49f630cSjeanPerier // Fortran 2018 18.5.3, pp3: BIND(C) non pointer allocatable descriptors 1361a49f630cSjeanPerier // must have zero lower bounds. 1362a49f630cSjeanPerier bool needsZeroLowerBounds = callContext.isBindcCall() && entity.isArray(); 136387cd6f93SJean Perier // On the callee side, the current code generated for unlimited 136487cd6f93SJean Perier // polymorphic might unconditionally read the addendum. Intrinsic type 136587cd6f93SJean Perier // descriptors may not have an addendum, the rebox below will create a 136687cd6f93SJean Perier // descriptor with an addendum in such case. 1367f5dbee00SSlava Zakharin const bool actualBoxHasAddendum = fir::boxHasAddendum(actualBoxType); 136887cd6f93SJean Perier const bool needToAddAddendum = 1369a49f630cSjeanPerier fir::isUnlimitedPolymorphicType(dummyTypeWithActualRank) && 1370a49f630cSjeanPerier !actualBoxHasAddendum; 1371a49f630cSjeanPerier if (needToAddAddendum || actualBoxHasAllocatableOrPointerFlag || 1372a49f630cSjeanPerier needsZeroLowerBounds) { 1373a49f630cSjeanPerier if (actualIsAssumedRank) { 1374453a0e4cSjeanPerier auto lbModifier = needsZeroLowerBounds 1375453a0e4cSjeanPerier ? fir::LowerBoundModifierAttribute::SetToZeroes 1376453a0e4cSjeanPerier : fir::LowerBoundModifierAttribute::SetToOnes; 1377453a0e4cSjeanPerier entity = hlfir::Entity{builder.create<fir::ReboxAssumedRankOp>( 1378453a0e4cSjeanPerier loc, dummyTypeWithActualRank, entity, lbModifier)}; 1379453a0e4cSjeanPerier } else { 1380a49f630cSjeanPerier mlir::Value shift{}; 1381a49f630cSjeanPerier if (needsZeroLowerBounds) 1382a49f630cSjeanPerier shift = getZeroLowerBounds(loc, builder, entity); 138387cd6f93SJean Perier entity = hlfir::Entity{builder.create<fir::ReboxOp>( 1384a49f630cSjeanPerier loc, dummyTypeWithActualRank, entity, /*shape=*/shift, 138587cd6f93SJean Perier /*slice=*/mlir::Value{})}; 1386f5dbee00SSlava Zakharin } 1387453a0e4cSjeanPerier } 138887cd6f93SJean Perier addr = entity; 138987cd6f93SJean Perier } else { 139087cd6f93SJean Perier addr = hlfir::genVariableRawAddress(loc, builder, entity); 139187cd6f93SJean Perier } 1392a49f630cSjeanPerier 1393a49f630cSjeanPerier // For ranked actual passed to assumed-rank dummy, the cast to assumed-rank 1394a49f630cSjeanPerier // box is inserted when building the fir.call op. Inserting it here would 1395a49f630cSjeanPerier // cause the fir.if results to be assumed-rank in case of OPTIONAL dummy, 1396a49f630cSjeanPerier // causing extra runtime costs due to the unknown runtime size of assumed-rank 1397a49f630cSjeanPerier // descriptors. 1398a49f630cSjeanPerier preparedDummy.dummy = 1399a49f630cSjeanPerier builder.createConvert(loc, dummyTypeWithActualRank, addr); 140087cd6f93SJean Perier return preparedDummy; 140187cd6f93SJean Perier } 140287cd6f93SJean Perier 140387cd6f93SJean Perier /// When dummy is not ALLOCATABLE, POINTER and is not passed in register, 140487cd6f93SJean Perier /// prepare the actual argument according to the interface, taking care 140587cd6f93SJean Perier /// of any optional aspect. 140687cd6f93SJean Perier static PreparedDummyArgument prepareUserCallActualArgument( 140787cd6f93SJean Perier mlir::Location loc, fir::FirOpBuilder &builder, 1408d2d21301STom Eccles const Fortran::lower::PreparedActualArgument &preparedActual, 1409d2d21301STom Eccles mlir::Type dummyType, 141087cd6f93SJean Perier const Fortran::lower::CallerInterface::PassedEntity &arg, 14112984699aSjeanPerier CallContext &callContext) { 141287cd6f93SJean Perier if (!preparedActual.handleDynamicOptional()) 14132984699aSjeanPerier return preparePresentUserCallActualArgument(loc, builder, preparedActual, 14142984699aSjeanPerier dummyType, arg, callContext); 141587cd6f93SJean Perier 141687cd6f93SJean Perier // Conditional dummy argument preparation. The actual may be absent 141787cd6f93SJean Perier // at runtime, causing any addressing, copy, and packaging to have 141887cd6f93SJean Perier // undefined behavior. 141987cd6f93SJean Perier // To simplify the handling of this case, the "normal" dummy preparation 142087cd6f93SJean Perier // helper is used, except its generated code is wrapped inside a 142187cd6f93SJean Perier // fir.if(present). 142287cd6f93SJean Perier mlir::Value isPresent = preparedActual.getIsPresent(); 142387cd6f93SJean Perier mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint(); 142487cd6f93SJean Perier 142587cd6f93SJean Perier // Code generated in a preparation block that will become the 142687cd6f93SJean Perier // "then" block in "if (present) then {} else {}". The reason 142787cd6f93SJean Perier // for this unusual if/then/else generation is that the number 142887cd6f93SJean Perier // and types of the if results will depend on how the argument 142987cd6f93SJean Perier // is prepared, and forecasting that here would be brittle. 143087cd6f93SJean Perier auto badIfOp = builder.create<fir::IfOp>(loc, dummyType, isPresent, 143187cd6f93SJean Perier /*withElseRegion=*/false); 143287cd6f93SJean Perier mlir::Block *preparationBlock = &badIfOp.getThenRegion().front(); 143387cd6f93SJean Perier builder.setInsertionPointToStart(preparationBlock); 143487cd6f93SJean Perier PreparedDummyArgument unconditionalDummy = 14356db45cc4SSlava Zakharin preparePresentUserCallActualArgument(loc, builder, preparedActual, 14362984699aSjeanPerier dummyType, arg, callContext); 143787cd6f93SJean Perier builder.restoreInsertionPoint(insertPt); 143887cd6f93SJean Perier 143987cd6f93SJean Perier // TODO: when forwarding an optional to an optional of the same kind 144087cd6f93SJean Perier // (i.e, unconditionalDummy.dummy was not created in preparationBlock), 144187cd6f93SJean Perier // the if/then/else generation could be skipped to improve the generated 144287cd6f93SJean Perier // code. 144387cd6f93SJean Perier 144487cd6f93SJean Perier // Now that the result types of the ifOp can be deduced, generate 144587cd6f93SJean Perier // the "real" ifOp (operation result types cannot be changed, so 144687cd6f93SJean Perier // badIfOp cannot be modified and used here). 144787cd6f93SJean Perier llvm::SmallVector<mlir::Type> ifOpResultTypes; 144887cd6f93SJean Perier ConditionallyPreparedDummy conditionalDummy(unconditionalDummy); 144987cd6f93SJean Perier auto ifOp = builder.create<fir::IfOp>(loc, conditionalDummy.getIfResulTypes(), 145087cd6f93SJean Perier isPresent, 145187cd6f93SJean Perier /*withElseRegion=*/true); 145287cd6f93SJean Perier // Move "preparationBlock" into the "then" of the new 145387cd6f93SJean Perier // fir.if operation and create fir.result propagating 145487cd6f93SJean Perier // unconditionalDummy. 145587cd6f93SJean Perier preparationBlock->moveBefore(&ifOp.getThenRegion().back()); 145687cd6f93SJean Perier ifOp.getThenRegion().back().erase(); 145787cd6f93SJean Perier builder.setInsertionPointToEnd(&ifOp.getThenRegion().front()); 145887cd6f93SJean Perier conditionalDummy.genThenResult(loc, builder); 145987cd6f93SJean Perier 146087cd6f93SJean Perier // Generate "else" branch with returning absent values. 146187cd6f93SJean Perier builder.setInsertionPointToStart(&ifOp.getElseRegion().front()); 146287cd6f93SJean Perier conditionalDummy.genElseResult(loc, builder); 146387cd6f93SJean Perier 146487cd6f93SJean Perier // Build dummy from IfOpResults. 146587cd6f93SJean Perier builder.setInsertionPointAfter(ifOp); 146687cd6f93SJean Perier PreparedDummyArgument result = 146787cd6f93SJean Perier conditionalDummy.getPreparedDummy(ifOp, unconditionalDummy); 146887cd6f93SJean Perier badIfOp->erase(); 146987cd6f93SJean Perier return result; 147087cd6f93SJean Perier } 1471e78e4a17SJean Perier 147209b4649eSjeanPerier /// Prepare actual argument for a procedure pointer dummy. 147309b4649eSjeanPerier static PreparedDummyArgument prepareProcedurePointerActualArgument( 147409b4649eSjeanPerier mlir::Location loc, fir::FirOpBuilder &builder, 147509b4649eSjeanPerier const Fortran::lower::PreparedActualArgument &preparedActual, 147609b4649eSjeanPerier mlir::Type dummyType, 147709b4649eSjeanPerier const Fortran::lower::CallerInterface::PassedEntity &arg, 14782984699aSjeanPerier CallContext &callContext) { 147909b4649eSjeanPerier 148009b4649eSjeanPerier // NULL() actual to procedure pointer dummy 14812984699aSjeanPerier if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>( 14822984699aSjeanPerier *arg.entity) && 148309b4649eSjeanPerier fir::isBoxProcAddressType(dummyType)) { 148409b4649eSjeanPerier auto boxTy{Fortran::lower::getUntypedBoxProcType(builder.getContext())}; 148509b4649eSjeanPerier auto tempBoxProc{builder.createTemporary(loc, boxTy)}; 148609b4649eSjeanPerier hlfir::Entity nullBoxProc( 148709b4649eSjeanPerier fir::factory::createNullBoxProc(builder, loc, boxTy)); 148809b4649eSjeanPerier builder.create<fir::StoreOp>(loc, nullBoxProc, tempBoxProc); 148909b4649eSjeanPerier return PreparedDummyArgument{tempBoxProc, /*cleanups=*/{}}; 149009b4649eSjeanPerier } 149109b4649eSjeanPerier hlfir::Entity actual = preparedActual.getActual(loc, builder); 149209b4649eSjeanPerier if (actual.isProcedurePointer()) 149309b4649eSjeanPerier return PreparedDummyArgument{actual, /*cleanups=*/{}}; 149409b4649eSjeanPerier assert(actual.isProcedure()); 149509b4649eSjeanPerier // Procedure actual to procedure pointer dummy. 149609b4649eSjeanPerier auto tempBoxProc{builder.createTemporary(loc, actual.getType())}; 149709b4649eSjeanPerier builder.create<fir::StoreOp>(loc, actual, tempBoxProc); 149809b4649eSjeanPerier return PreparedDummyArgument{tempBoxProc, /*cleanups=*/{}}; 149909b4649eSjeanPerier } 150009b4649eSjeanPerier 1501c232137dSValentin Clement (バレンタイン クレメン) /// Prepare arguments of calls to user procedures with actual arguments that 1502c232137dSValentin Clement (バレンタイン クレメン) /// have been pre-lowered but not yet prepared according to the interface. 1503c232137dSValentin Clement (バレンタイン クレメン) void prepareUserCallArguments( 1504c232137dSValentin Clement (バレンタイン クレメン) Fortran::lower::PreparedActualArguments &loweredActuals, 1505c232137dSValentin Clement (バレンタイン クレメン) Fortran::lower::CallerInterface &caller, mlir::FunctionType callSiteType, 1506c232137dSValentin Clement (バレンタイン クレメン) CallContext &callContext, llvm::SmallVector<CallCleanUp> &callCleanUps) { 15073909b60aSJean Perier using PassBy = Fortran::lower::CallerInterface::PassEntityBy; 15083909b60aSJean Perier mlir::Location loc = callContext.loc; 15098eee2360SjeanPerier bool mustRemapActualToDummyDescriptors = false; 15103909b60aSJean Perier fir::FirOpBuilder &builder = callContext.getBuilder(); 15114203b062SJean Perier for (auto [preparedActual, arg] : 1512e78e4a17SJean Perier llvm::zip(loweredActuals, caller.getPassedArguments())) { 1513e78e4a17SJean Perier mlir::Type argTy = callSiteType.getInput(arg.firArgument); 15144203b062SJean Perier if (!preparedActual) { 1515e78e4a17SJean Perier // Optional dummy argument for which there is no actual argument. 151666ec3263SLeandro Lupori caller.placeInput(arg, builder.genAbsentOp(loc, argTy)); 1517e78e4a17SJean Perier continue; 1518e78e4a17SJean Perier } 1519e78e4a17SJean Perier 1520e78e4a17SJean Perier switch (arg.passBy) { 1521e78e4a17SJean Perier case PassBy::Value: { 1522e78e4a17SJean Perier // True pass-by-value semantics. 152387cd6f93SJean Perier assert(!preparedActual->handleDynamicOptional() && "cannot be optional"); 152487cd6f93SJean Perier hlfir::Entity actual = preparedActual->getActual(loc, builder); 1525882e5f7bSSlava Zakharin hlfir::Entity value = hlfir::loadTrivialScalar(loc, builder, actual); 1526882e5f7bSSlava Zakharin 1527882e5f7bSSlava Zakharin mlir::Type eleTy = value.getFortranElementType(); 1528882e5f7bSSlava Zakharin if (fir::isa_builtin_cptr_type(eleTy)) { 1529882e5f7bSSlava Zakharin // Pass-by-value argument of type(C_PTR/C_FUNPTR). 1530882e5f7bSSlava Zakharin // Load the __address component and pass it by value. 1531882e5f7bSSlava Zakharin if (value.isValue()) { 1532882e5f7bSSlava Zakharin auto associate = hlfir::genAssociateExpr(loc, builder, value, eleTy, 1533882e5f7bSSlava Zakharin "adapt.cptrbyval"); 1534882e5f7bSSlava Zakharin value = hlfir::Entity{genRecordCPtrValueArg( 1535882e5f7bSSlava Zakharin builder, loc, associate.getFirBase(), eleTy)}; 1536882e5f7bSSlava Zakharin builder.create<hlfir::EndAssociateOp>(loc, associate); 1537882e5f7bSSlava Zakharin } else { 1538882e5f7bSSlava Zakharin value = 1539882e5f7bSSlava Zakharin hlfir::Entity{genRecordCPtrValueArg(builder, loc, value, eleTy)}; 1540882e5f7bSSlava Zakharin } 1541ad4e1abaSjeanPerier } else if (fir::isa_derived(value.getFortranElementType()) || 1542ad4e1abaSjeanPerier value.isCharacter()) { 1543ad4e1abaSjeanPerier // BIND(C), VALUE derived type or character. The value must really 1544dfb7d56aSjeanPerier // be loaded here. 1545ad4e1abaSjeanPerier auto [exv, cleanup] = hlfir::convertToValue(loc, builder, value); 1546ad4e1abaSjeanPerier mlir::Value loadedValue = fir::getBase(exv); 1547ad4e1abaSjeanPerier // Character actual arguments may have unknown length or a length longer 1548ad4e1abaSjeanPerier // than one. Cast the memory ref to the dummy type so that the load is 1549ad4e1abaSjeanPerier // valid and only loads what is needed. 1550ad4e1abaSjeanPerier if (mlir::Type baseTy = fir::dyn_cast_ptrEleTy(loadedValue.getType())) 1551ad4e1abaSjeanPerier if (fir::isa_char(baseTy)) 1552ad4e1abaSjeanPerier loadedValue = builder.createConvert( 1553ad4e1abaSjeanPerier loc, fir::ReferenceType::get(argTy), loadedValue); 1554dfb7d56aSjeanPerier if (fir::isa_ref_type(loadedValue.getType())) 1555dfb7d56aSjeanPerier loadedValue = builder.create<fir::LoadOp>(loc, loadedValue); 1556dfb7d56aSjeanPerier caller.placeInput(arg, loadedValue); 1557dfb7d56aSjeanPerier if (cleanup) 1558dfb7d56aSjeanPerier (*cleanup)(); 1559dfb7d56aSjeanPerier break; 1560882e5f7bSSlava Zakharin } 1561e78e4a17SJean Perier caller.placeInput(arg, builder.createConvert(loc, argTy, value)); 1562e78e4a17SJean Perier } break; 156387cd6f93SJean Perier case PassBy::BaseAddressValueAttribute: 156487cd6f93SJean Perier case PassBy::CharBoxValueAttribute: 156587cd6f93SJean Perier case PassBy::Box: 1566e78e4a17SJean Perier case PassBy::BaseAddress: 1567e78e4a17SJean Perier case PassBy::BoxChar: { 1568a49f630cSjeanPerier PreparedDummyArgument preparedDummy = prepareUserCallActualArgument( 15692984699aSjeanPerier loc, builder, *preparedActual, argTy, arg, callContext); 1570ab340f97SSlava Zakharin callCleanUps.append(preparedDummy.cleanups.rbegin(), 1571ab340f97SSlava Zakharin preparedDummy.cleanups.rend()); 157287cd6f93SJean Perier caller.placeInput(arg, preparedDummy.dummy); 15738eee2360SjeanPerier if (arg.passBy == PassBy::Box) 15748eee2360SjeanPerier mustRemapActualToDummyDescriptors |= 15758eee2360SjeanPerier arg.isSequenceAssociatedDescriptor(); 1576e78e4a17SJean Perier } break; 157709b4649eSjeanPerier case PassBy::BoxProcRef: { 157809b4649eSjeanPerier PreparedDummyArgument preparedDummy = 157909b4649eSjeanPerier prepareProcedurePointerActualArgument(loc, builder, *preparedActual, 15802984699aSjeanPerier argTy, arg, callContext); 158109b4649eSjeanPerier callCleanUps.append(preparedDummy.cleanups.rbegin(), 158209b4649eSjeanPerier preparedDummy.cleanups.rend()); 158309b4649eSjeanPerier caller.placeInput(arg, preparedDummy.dummy); 158409b4649eSjeanPerier } break; 1585e78e4a17SJean Perier case PassBy::AddressAndLength: 1586e78e4a17SJean Perier // PassBy::AddressAndLength is only used for character results. Results 1587e78e4a17SJean Perier // are not handled here. 1588e78e4a17SJean Perier fir::emitFatalError( 1589e78e4a17SJean Perier loc, "unexpected PassBy::AddressAndLength for actual arguments"); 1590e78e4a17SJean Perier break; 1591e78e4a17SJean Perier case PassBy::CharProcTuple: { 1592cedfd272SJean Perier hlfir::Entity actual = preparedActual->getActual(loc, builder); 1593af09219eSDaniel Chen if (actual.isProcedurePointer()) 1594af09219eSDaniel Chen actual = hlfir::derefPointersAndAllocatables(loc, builder, actual); 159592e904b9SJean Perier if (!fir::isCharacterProcedureTuple(actual.getType())) 159692e904b9SJean Perier actual = fixProcedureDummyMismatch(loc, builder, actual, argTy); 1597cedfd272SJean Perier caller.placeInput(arg, actual); 1598e78e4a17SJean Perier } break; 1599e78e4a17SJean Perier case PassBy::MutableBox: { 16002984699aSjeanPerier const Fortran::lower::SomeExpr *expr = arg.entity->UnwrapExpr(); 16012984699aSjeanPerier // C709 and C710. 16022984699aSjeanPerier assert(expr && "cannot pass TYPE(*) to POINTER or ALLOCATABLE"); 160387cd6f93SJean Perier hlfir::Entity actual = preparedActual->getActual(loc, builder); 16041119c15eSJean Perier if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>( 16051119c15eSJean Perier *expr)) { 16061119c15eSJean Perier // If expr is NULL(), the mutableBox created must be a deallocated 16071119c15eSJean Perier // pointer with the dummy argument characteristics (see table 16.5 16081119c15eSJean Perier // in Fortran 2018 standard). 16091119c15eSJean Perier // No length parameters are set for the created box because any non 16101119c15eSJean Perier // deferred type parameters of the dummy will be evaluated on the 16111119c15eSJean Perier // callee side, and it is illegal to use NULL without a MOLD if any 16121119c15eSJean Perier // dummy length parameters are assumed. 16131119c15eSJean Perier mlir::Type boxTy = fir::dyn_cast_ptrEleTy(argTy); 1614fac349a1SChristian Sigg assert(boxTy && mlir::isa<fir::BaseBoxType>(boxTy) && 1615fdce1b7eSSlava Zakharin "must be a fir.box type"); 1616498f706bSSlava Zakharin mlir::Value boxStorage = 1617498f706bSSlava Zakharin fir::factory::genNullBoxStorage(builder, loc, boxTy); 16181119c15eSJean Perier caller.placeInput(arg, boxStorage); 16191119c15eSJean Perier continue; 16201119c15eSJean Perier } 16211119c15eSJean Perier if (fir::isPointerType(argTy) && 1622f025e411SPeter Klausler !Fortran::evaluate::IsObjectPointer(*expr)) { 16231119c15eSJean Perier // Passing a non POINTER actual argument to a POINTER dummy argument. 16241119c15eSJean Perier // Create a pointer of the dummy argument type and assign the actual 16251119c15eSJean Perier // argument to it. 1626a49f630cSjeanPerier auto dataTy = llvm::cast<fir::BaseBoxType>(fir::unwrapRefType(argTy)); 16274ef04876SSlava Zakharin fir::ExtendedValue actualExv = Fortran::lower::convertToAddress( 16284ef04876SSlava Zakharin loc, callContext.converter, actual, callContext.stmtCtx, 16294ef04876SSlava Zakharin hlfir::getFortranElementType(dataTy)); 1630a49f630cSjeanPerier // If the dummy is an assumed-rank pointer, allocate a pointer 1631a49f630cSjeanPerier // descriptor with the actual argument rank (if it is not assumed-rank 1632a49f630cSjeanPerier // itself). 1633a49f630cSjeanPerier if (dataTy.isAssumedRank()) { 1634a49f630cSjeanPerier dataTy = 1635a49f630cSjeanPerier dataTy.getBoxTypeWithNewShape(fir::getBase(actualExv).getType()); 1636a49f630cSjeanPerier } 16374ef04876SSlava Zakharin mlir::Value irBox = builder.createTemporary(loc, dataTy); 16384ef04876SSlava Zakharin fir::MutableBoxValue ptrBox(irBox, 16394ef04876SSlava Zakharin /*nonDeferredParams=*/mlir::ValueRange{}, 16404ef04876SSlava Zakharin /*mutableProperties=*/{}); 16414ef04876SSlava Zakharin fir::factory::associateMutableBox(builder, loc, ptrBox, actualExv, 16424ef04876SSlava Zakharin /*lbounds=*/std::nullopt); 16434ef04876SSlava Zakharin caller.placeInput(arg, irBox); 16441119c15eSJean Perier continue; 16451119c15eSJean Perier } 16461119c15eSJean Perier // Passing a POINTER to a POINTER, or an ALLOCATABLE to an ALLOCATABLE. 16471119c15eSJean Perier assert(actual.isMutableBox() && "actual must be a mutable box"); 16481119c15eSJean Perier if (fir::isAllocatableType(argTy) && arg.isIntentOut() && 1649a49f630cSjeanPerier callContext.isBindcCall()) { 1650e5cb6da7SjeanPerier // INTENT(OUT) allocatables are deallocated on the callee side, 1651e5cb6da7SjeanPerier // but BIND(C) procedures may be implemented in C, so deallocation is 1652e5cb6da7SjeanPerier // also done on the caller side (if the procedure is implemented in 1653e5cb6da7SjeanPerier // Fortran, the deallocation attempt in the callee will be a no-op). 1654e5cb6da7SjeanPerier auto [exv, cleanup] = 1655e5cb6da7SjeanPerier hlfir::translateToExtendedValue(loc, builder, actual); 1656e5cb6da7SjeanPerier const auto *mutableBox = exv.getBoxOf<fir::MutableBoxValue>(); 1657e5cb6da7SjeanPerier assert(mutableBox && !cleanup && "expect allocatable"); 1658e5cb6da7SjeanPerier Fortran::lower::genDeallocateIfAllocated(callContext.converter, 1659e5cb6da7SjeanPerier *mutableBox, loc); 16601119c15eSJean Perier } 1661e5cb6da7SjeanPerier caller.placeInput(arg, actual); 1662e78e4a17SJean Perier } break; 1663e78e4a17SJean Perier } 1664e78e4a17SJean Perier } 1665c232137dSValentin Clement (バレンタイン クレメン) 16668eee2360SjeanPerier // Handle cases where caller must allocate the result or a fir.box for it. 16678eee2360SjeanPerier if (mustRemapActualToDummyDescriptors) 16688eee2360SjeanPerier remapActualToDummyDescriptors(loc, callContext.converter, 16698eee2360SjeanPerier callContext.symMap, loweredActuals, caller, 16708eee2360SjeanPerier callContext.isBindcCall()); 1671c232137dSValentin Clement (バレンタイン クレメン) } 1672c232137dSValentin Clement (バレンタイン クレメン) 1673c232137dSValentin Clement (バレンタイン クレメン) /// Lower calls to user procedures with actual arguments that have been 1674c232137dSValentin Clement (バレンタイン クレメン) /// pre-lowered but not yet prepared according to the interface. 1675c232137dSValentin Clement (バレンタイン クレメン) /// This can be called for elemental procedures, but only with scalar 1676c232137dSValentin Clement (バレンタイン クレメン) /// arguments: if there are array arguments, it must be provided with 1677c232137dSValentin Clement (バレンタイン クレメン) /// the array argument elements value and will return the corresponding 1678c232137dSValentin Clement (バレンタイン クレメン) /// scalar result value. 1679c232137dSValentin Clement (バレンタイン クレメン) static std::optional<hlfir::EntityWithAttributes> 1680c232137dSValentin Clement (バレンタイン クレメン) genUserCall(Fortran::lower::PreparedActualArguments &loweredActuals, 1681c232137dSValentin Clement (バレンタイン クレメン) Fortran::lower::CallerInterface &caller, 1682c232137dSValentin Clement (バレンタイン クレメン) mlir::FunctionType callSiteType, CallContext &callContext) { 1683c232137dSValentin Clement (バレンタイン クレメン) mlir::Location loc = callContext.loc; 1684c232137dSValentin Clement (バレンタイン クレメン) llvm::SmallVector<CallCleanUp> callCleanUps; 1685c232137dSValentin Clement (バレンタイン クレメン) fir::FirOpBuilder &builder = callContext.getBuilder(); 1686c232137dSValentin Clement (バレンタイン クレメン) 1687c232137dSValentin Clement (バレンタイン クレメン) prepareUserCallArguments(loweredActuals, caller, callSiteType, callContext, 1688c232137dSValentin Clement (バレンタイン クレメン) callCleanUps); 16898eee2360SjeanPerier 1690e78e4a17SJean Perier // Prepare lowered arguments according to the interface 1691e78e4a17SJean Perier // and map the lowered values to the dummy 1692e78e4a17SJean Perier // arguments. 1693*cd7e6539SjeanPerier auto [loweredResult, resultIsFinalized] = Fortran::lower::genCallOpAndResult( 16943909b60aSJean Perier loc, callContext.converter, callContext.symMap, callContext.stmtCtx, 1695ab1db262SSlava Zakharin caller, callSiteType, callContext.resultType, 1696ab1db262SSlava Zakharin callContext.isElementalProcWithArrayArgs()); 1697e78e4a17SJean Perier 1698e78e4a17SJean Perier /// Clean-up associations and copy-in. 169987cd6f93SJean Perier for (auto cleanUp : callCleanUps) 170087cd6f93SJean Perier cleanUp.genCleanUp(loc, builder); 170187cd6f93SJean Perier 1702*cd7e6539SjeanPerier if (auto *entity = std::get_if<hlfir::EntityWithAttributes>(&loweredResult)) 1703*cd7e6539SjeanPerier return *entity; 1704*cd7e6539SjeanPerier 1705*cd7e6539SjeanPerier auto &result = std::get<fir::ExtendedValue>(loweredResult); 1706*cd7e6539SjeanPerier 1707*cd7e6539SjeanPerier // For procedure pointer function result, just return the call. 1708*cd7e6539SjeanPerier if (callContext.resultType && 1709*cd7e6539SjeanPerier mlir::isa<fir::BoxProcType>(*callContext.resultType)) 1710*cd7e6539SjeanPerier return hlfir::EntityWithAttributes(fir::getBase(result)); 1711*cd7e6539SjeanPerier 1712b013ebe0SJean Perier if (!fir::getBase(result)) 17139a417395SKazu Hirata return std::nullopt; // subroutine call. 171481ea91a9SSlava Zakharin 171567402fe5SjeanPerier if (fir::isPointerType(fir::getBase(result).getType())) 171667402fe5SjeanPerier return extendedValueToHlfirEntity(loc, builder, result, tempResultName); 171767402fe5SjeanPerier 171867402fe5SjeanPerier if (!resultIsFinalized) { 171981ea91a9SSlava Zakharin hlfir::Entity resultEntity = 172067402fe5SjeanPerier extendedValueToHlfirEntity(loc, builder, result, tempResultName); 172181ea91a9SSlava Zakharin resultEntity = loadTrivialScalar(loc, builder, resultEntity); 172281ea91a9SSlava Zakharin if (resultEntity.isVariable()) { 172367402fe5SjeanPerier // If the result has no finalization, it can be moved into an expression. 172467402fe5SjeanPerier // In such case, the expression should not be freed after its use since 172567402fe5SjeanPerier // the result is stack allocated or deallocation (for allocatable results) 172667402fe5SjeanPerier // was already inserted in genCallOpAndResult. 172781ea91a9SSlava Zakharin auto asExpr = builder.create<hlfir::AsExprOp>( 172881ea91a9SSlava Zakharin loc, resultEntity, /*mustFree=*/builder.createBool(loc, false)); 172967402fe5SjeanPerier return hlfir::EntityWithAttributes{asExpr.getResult()}; 173081ea91a9SSlava Zakharin } 173181ea91a9SSlava Zakharin return hlfir::EntityWithAttributes{resultEntity}; 1732e78e4a17SJean Perier } 173367402fe5SjeanPerier // If the result has finalization, it cannot be moved because use of its 173467402fe5SjeanPerier // value have been created in the statement context and may be emitted 173567402fe5SjeanPerier // after the hlfir.expr destroy, so the result is kept as a variable in 173667402fe5SjeanPerier // HLFIR. This may lead to copies when passing the result to an argument 173767402fe5SjeanPerier // with VALUE, and this do not convey the fact that the result will not 173867402fe5SjeanPerier // change, but is correct, and using hlfir.expr without the move would 173967402fe5SjeanPerier // trigger a copy that may be avoided. 174067402fe5SjeanPerier 174167402fe5SjeanPerier // Load allocatable results before emitting the hlfir.declare and drop its 174267402fe5SjeanPerier // lower bounds: this is not a variable From the Fortran point of view, so 174367402fe5SjeanPerier // the lower bounds are ones when inquired on the caller side. 174467402fe5SjeanPerier const auto *allocatable = result.getBoxOf<fir::MutableBoxValue>(); 174567402fe5SjeanPerier fir::ExtendedValue loadedResult = 174667402fe5SjeanPerier allocatable 174767402fe5SjeanPerier ? fir::factory::genMutableBoxRead(builder, loc, *allocatable, 174867402fe5SjeanPerier /*mayBePolymorphic=*/true, 174967402fe5SjeanPerier /*preserveLowerBounds=*/false) 175067402fe5SjeanPerier : result; 175167402fe5SjeanPerier return extendedValueToHlfirEntity(loc, builder, loadedResult, tempResultName); 175267402fe5SjeanPerier } 1753e78e4a17SJean Perier 17545c428079SSlava Zakharin /// Create an optional dummy argument value from an entity that may be 17555c428079SSlava Zakharin /// absent. \p actualGetter callback returns hlfir::Entity denoting 17565c428079SSlava Zakharin /// the lowered actual argument. \p actualGetter can only return numerical 17575c428079SSlava Zakharin /// or logical scalar entity. 17585c428079SSlava Zakharin /// If the entity is considered absent according to 15.5.2.12 point 1., the 17595c428079SSlava Zakharin /// returned value is zero (or false), otherwise it is the value of the entity. 17605c428079SSlava Zakharin /// \p eleType specifies the entity's Fortran element type. 17615c428079SSlava Zakharin template <typename T> 17621b74faddSTom Eccles static ExvAndCleanup genOptionalValue(fir::FirOpBuilder &builder, 17635c428079SSlava Zakharin mlir::Location loc, mlir::Type eleType, 17645c428079SSlava Zakharin T actualGetter, mlir::Value isPresent) { 17651b74faddSTom Eccles return {builder 17661b74faddSTom Eccles .genIfOp(loc, {eleType}, isPresent, 17671b74faddSTom Eccles /*withElseRegion=*/true) 17681b74faddSTom Eccles .genThen([&]() { 17695c428079SSlava Zakharin hlfir::Entity entity = actualGetter(loc, builder); 17705c428079SSlava Zakharin assert(eleType == entity.getFortranElementType() && 17715c428079SSlava Zakharin "result type mismatch in genOptionalValue"); 17725c428079SSlava Zakharin assert(entity.isScalar() && fir::isa_trivial(eleType) && 17735c428079SSlava Zakharin "must be a numerical or logical scalar"); 17741b74faddSTom Eccles mlir::Value val = 17751b74faddSTom Eccles hlfir::loadTrivialScalar(loc, builder, entity); 17761b74faddSTom Eccles builder.create<fir::ResultOp>(loc, val); 17771b74faddSTom Eccles }) 17781b74faddSTom Eccles .genElse([&]() { 17791b74faddSTom Eccles mlir::Value zero = 17801b74faddSTom Eccles fir::factory::createZeroValue(builder, loc, eleType); 17811b74faddSTom Eccles builder.create<fir::ResultOp>(loc, zero); 17821b74faddSTom Eccles }) 17831b74faddSTom Eccles .getResults()[0], 17841b74faddSTom Eccles std::nullopt}; 17851b74faddSTom Eccles } 17861b74faddSTom Eccles 17871b74faddSTom Eccles /// Create an optional dummy argument address from \p entity that may be 17881b74faddSTom Eccles /// absent. If \p entity is considered absent according to 15.5.2.12 point 1., 17891b74faddSTom Eccles /// the returned value is a null pointer, otherwise it is the address of \p 17901b74faddSTom Eccles /// entity. 17911b74faddSTom Eccles static ExvAndCleanup genOptionalAddr(fir::FirOpBuilder &builder, 17921b74faddSTom Eccles mlir::Location loc, hlfir::Entity entity, 17931b74faddSTom Eccles mlir::Value isPresent) { 17941b74faddSTom Eccles auto [exv, cleanup] = hlfir::translateToExtendedValue(loc, builder, entity); 17951b74faddSTom Eccles // If it is an exv pointer/allocatable, then it cannot be absent 17961b74faddSTom Eccles // because it is passed to a non-pointer/non-allocatable. 17971b74faddSTom Eccles if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>()) 17981b74faddSTom Eccles return {fir::factory::genMutableBoxRead(builder, loc, *box), cleanup}; 17991b74faddSTom Eccles // If this is not a POINTER or ALLOCATABLE, then it is already an OPTIONAL 18001b74faddSTom Eccles // address and can be passed directly. 18011b74faddSTom Eccles return {exv, cleanup}; 18021b74faddSTom Eccles } 18031b74faddSTom Eccles 18041b74faddSTom Eccles /// Create an optional dummy argument address from \p entity that may be 18051b74faddSTom Eccles /// absent. If \p entity is considered absent according to 15.5.2.12 point 1., 18061b74faddSTom Eccles /// the returned value is an absent fir.box, otherwise it is a fir.box 18071b74faddSTom Eccles /// describing \p entity. 18081b74faddSTom Eccles static ExvAndCleanup genOptionalBox(fir::FirOpBuilder &builder, 18091b74faddSTom Eccles mlir::Location loc, hlfir::Entity entity, 18101b74faddSTom Eccles mlir::Value isPresent) { 18111b74faddSTom Eccles auto [exv, cleanup] = hlfir::translateToExtendedValue(loc, builder, entity); 18121b74faddSTom Eccles 18131b74faddSTom Eccles // Non allocatable/pointer optional box -> simply forward 18141b74faddSTom Eccles if (exv.getBoxOf<fir::BoxValue>()) 18151b74faddSTom Eccles return {exv, cleanup}; 18161b74faddSTom Eccles 18171b74faddSTom Eccles fir::ExtendedValue newExv = exv; 18181b74faddSTom Eccles // Optional allocatable/pointer -> Cannot be absent, but need to translate 18191b74faddSTom Eccles // unallocated/diassociated into absent fir.box. 18201b74faddSTom Eccles if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>()) 18211b74faddSTom Eccles newExv = fir::factory::genMutableBoxRead(builder, loc, *box); 18221b74faddSTom Eccles 18231b74faddSTom Eccles // createBox will not do create any invalid memory dereferences if exv is 18241b74faddSTom Eccles // absent. The created fir.box will not be usable, but the SelectOp below 18251b74faddSTom Eccles // ensures it won't be. 18261b74faddSTom Eccles mlir::Value box = builder.createBox(loc, newExv); 18271b74faddSTom Eccles mlir::Type boxType = box.getType(); 18281b74faddSTom Eccles auto absent = builder.create<fir::AbsentOp>(loc, boxType); 18291b74faddSTom Eccles auto boxOrAbsent = builder.create<mlir::arith::SelectOp>( 18301b74faddSTom Eccles loc, boxType, isPresent, box, absent); 18311b74faddSTom Eccles return {fir::BoxValue(boxOrAbsent), cleanup}; 18321b74faddSTom Eccles } 18331b74faddSTom Eccles 1834db7b665cSTom Eccles /// Lower calls to intrinsic procedures with custom optional handling where the 1835db7b665cSTom Eccles /// actual arguments have been pre-lowered 1836db7b665cSTom Eccles static std::optional<hlfir::EntityWithAttributes> genCustomIntrinsicRefCore( 1837db7b665cSTom Eccles Fortran::lower::PreparedActualArguments &loweredActuals, 1838db7b665cSTom Eccles const Fortran::evaluate::SpecificIntrinsic *intrinsic, 1839db7b665cSTom Eccles CallContext &callContext) { 1840db7b665cSTom Eccles auto &builder = callContext.getBuilder(); 1841db7b665cSTom Eccles const auto &loc = callContext.loc; 18420ccef6a7SMats Petersson assert(intrinsic && 18430ccef6a7SMats Petersson Fortran::lower::intrinsicRequiresCustomOptionalHandling( 184430d11e5eSJie Fu callContext.procRef, *intrinsic, callContext.converter)); 1845db7b665cSTom Eccles 1846db7b665cSTom Eccles // helper to get a particular prepared argument 1847db7b665cSTom Eccles auto getArgument = [&](std::size_t i, bool loadArg) -> fir::ExtendedValue { 1848db7b665cSTom Eccles if (!loweredActuals[i]) 1849db7b665cSTom Eccles return fir::getAbsentIntrinsicArgument(); 1850db7b665cSTom Eccles hlfir::Entity actual = loweredActuals[i]->getActual(loc, builder); 1851db7b665cSTom Eccles if (loadArg && fir::conformsWithPassByRef(actual.getType())) { 1852db7b665cSTom Eccles return hlfir::loadTrivialScalar(loc, builder, actual); 1853db7b665cSTom Eccles } 1854f55622f0SValentin Clement (バレンタイン クレメン) return Fortran::lower::translateToExtendedValue(loc, builder, actual, 1855f55622f0SValentin Clement (バレンタイン クレメン) callContext.stmtCtx); 1856db7b665cSTom Eccles }; 1857db7b665cSTom Eccles // helper to get the isPresent flag for a particular prepared argument 1858db7b665cSTom Eccles auto isPresent = [&](std::size_t i) -> std::optional<mlir::Value> { 1859db7b665cSTom Eccles if (!loweredActuals[i]) 1860db7b665cSTom Eccles return {builder.createBool(loc, false)}; 1861db7b665cSTom Eccles if (loweredActuals[i]->handleDynamicOptional()) 1862db7b665cSTom Eccles return {loweredActuals[i]->getIsPresent()}; 1863db7b665cSTom Eccles return std::nullopt; 1864db7b665cSTom Eccles }; 1865db7b665cSTom Eccles 1866db7b665cSTom Eccles assert(callContext.resultType && 1867db7b665cSTom Eccles "the elemental intrinsics with custom handling are all functions"); 1868db7b665cSTom Eccles // if callContext.resultType is an array then this was originally an elemental 1869db7b665cSTom Eccles // call. What we are lowering here is inside the kernel of the hlfir.elemental 1870db7b665cSTom Eccles // so we should return the scalar type. If the return type is already a scalar 1871db7b665cSTom Eccles // then it should be unchanged here. 1872db7b665cSTom Eccles mlir::Type resTy = hlfir::getFortranElementType(*callContext.resultType); 1873db7b665cSTom Eccles fir::ExtendedValue result = Fortran::lower::lowerCustomIntrinsic( 1874db7b665cSTom Eccles builder, loc, callContext.getProcedureName(), resTy, isPresent, 1875db7b665cSTom Eccles getArgument, loweredActuals.size(), callContext.stmtCtx); 1876db7b665cSTom Eccles 1877db7b665cSTom Eccles return {hlfir::EntityWithAttributes{extendedValueToHlfirEntity( 1878db7b665cSTom Eccles loc, builder, result, ".tmp.custom_intrinsic_result")}}; 1879db7b665cSTom Eccles } 1880db7b665cSTom Eccles 1881199e4974SJean Perier /// Lower calls to intrinsic procedures with actual arguments that have been 1882199e4974SJean Perier /// pre-lowered but have not yet been prepared according to the interface. 18836dcb31deSTom Eccles static std::optional<hlfir::EntityWithAttributes> 1884d2d21301STom Eccles genIntrinsicRefCore(Fortran::lower::PreparedActualArguments &loweredActuals, 18856ed4a8b9SJean Perier const Fortran::evaluate::SpecificIntrinsic *intrinsic, 1886727ecaf7SjeanPerier const fir::IntrinsicHandlerEntry &intrinsicEntry, 1887c0b45fefSJean Perier CallContext &callContext) { 1888db7b665cSTom Eccles auto &converter = callContext.converter; 1889db7b665cSTom Eccles if (intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling( 1890db7b665cSTom Eccles callContext.procRef, *intrinsic, converter)) 1891db7b665cSTom Eccles return genCustomIntrinsicRefCore(loweredActuals, intrinsic, callContext); 1892199e4974SJean Perier llvm::SmallVector<fir::ExtendedValue> operands; 18931b74faddSTom Eccles llvm::SmallVector<hlfir::CleanupFunction> cleanupFns; 18941b74faddSTom Eccles auto addToCleanups = [&cleanupFns](std::optional<hlfir::CleanupFunction> fn) { 18951b74faddSTom Eccles if (fn) 18961b74faddSTom Eccles cleanupFns.emplace_back(std::move(*fn)); 18971b74faddSTom Eccles }; 1898199e4974SJean Perier auto &stmtCtx = callContext.stmtCtx; 189987cd6f93SJean Perier fir::FirOpBuilder &builder = callContext.getBuilder(); 1900199e4974SJean Perier mlir::Location loc = callContext.loc; 1901727ecaf7SjeanPerier const fir::IntrinsicArgumentLoweringRules *argLowering = 1902727ecaf7SjeanPerier intrinsicEntry.getArgumentLoweringRules(); 1903199e4974SJean Perier for (auto arg : llvm::enumerate(loweredActuals)) { 1904199e4974SJean Perier if (!arg.value()) { 19056dcb31deSTom Eccles operands.emplace_back(fir::getAbsentIntrinsicArgument()); 1906199e4974SJean Perier continue; 1907199e4974SJean Perier } 1908199e4974SJean Perier if (!argLowering) { 1909199e4974SJean Perier // No argument lowering instruction, lower by value. 191075f459d5STom Eccles assert(!arg.value()->handleDynamicOptional() && 191175f459d5STom Eccles "should use genOptionalValue"); 19125c428079SSlava Zakharin hlfir::Entity actual = arg.value()->getActual(loc, builder); 1913199e4974SJean Perier operands.emplace_back( 1914199e4974SJean Perier Fortran::lower::convertToValue(loc, converter, actual, stmtCtx)); 1915199e4974SJean Perier continue; 1916199e4974SJean Perier } 1917ff2912a0SJean Perier // Helper to get the type of the Fortran expression in case it is a 1918ff2912a0SJean Perier // computed value that must be placed in memory (logicals are computed as 1919ff2912a0SJean Perier // i1, but must be placed in memory as fir.logical). 192032983aa0SJean Perier auto getActualFortranElementType = [&]() -> mlir::Type { 192132983aa0SJean Perier if (const Fortran::lower::SomeExpr *expr = 192232983aa0SJean Perier callContext.procRef.UnwrapArgExpr(arg.index())) { 192332983aa0SJean Perier 1924ff2912a0SJean Perier mlir::Type type = converter.genType(*expr); 1925ff2912a0SJean Perier return hlfir::getFortranElementType(type); 192632983aa0SJean Perier } 192732983aa0SJean Perier // TYPE(*): is already in memory anyway. Can return none 192832983aa0SJean Perier // here. 192932983aa0SJean Perier return builder.getNoneType(); 1930ff2912a0SJean Perier }; 1931199e4974SJean Perier // Ad-hoc argument lowering handling. 19326dcb31deSTom Eccles fir::ArgLoweringRule argRules = 19336dcb31deSTom Eccles fir::lowerIntrinsicArgumentAs(*argLowering, arg.index()); 19341b74faddSTom Eccles if (arg.value()->handleDynamicOptional()) { 19351b74faddSTom Eccles mlir::Value isPresent = arg.value()->getIsPresent(); 19361b74faddSTom Eccles switch (argRules.lowerAs) { 19371b74faddSTom Eccles case fir::LowerIntrinsicArgAs::Value: { 19385c428079SSlava Zakharin // In case of elemental call, getActual() may produce 19395c428079SSlava Zakharin // a designator denoting the array element to be passed 19405c428079SSlava Zakharin // to the subprogram. If the actual array is dynamically 19415c428079SSlava Zakharin // optional the designator must be generated under 19425c428079SSlava Zakharin // isPresent check, because the box bounds reads will be 19435c428079SSlava Zakharin // generated in the codegen. These reads are illegal, 19445c428079SSlava Zakharin // if the dynamically optional argument is absent. 19455c428079SSlava Zakharin auto getActualCb = [&](mlir::Location loc, 19465c428079SSlava Zakharin fir::FirOpBuilder &builder) -> hlfir::Entity { 19475c428079SSlava Zakharin return arg.value()->getActual(loc, builder); 19485c428079SSlava Zakharin }; 19495c428079SSlava Zakharin auto [exv, cleanup] = 19505c428079SSlava Zakharin genOptionalValue(builder, loc, getActualFortranElementType(), 19515c428079SSlava Zakharin getActualCb, isPresent); 19521b74faddSTom Eccles addToCleanups(std::move(cleanup)); 19531b74faddSTom Eccles operands.emplace_back(exv); 19541b74faddSTom Eccles continue; 19551b74faddSTom Eccles } 19561b74faddSTom Eccles case fir::LowerIntrinsicArgAs::Addr: { 19575c428079SSlava Zakharin hlfir::Entity actual = arg.value()->getActual(loc, builder); 19581b74faddSTom Eccles auto [exv, cleanup] = genOptionalAddr(builder, loc, actual, isPresent); 19591b74faddSTom Eccles addToCleanups(std::move(cleanup)); 19601b74faddSTom Eccles operands.emplace_back(exv); 19611b74faddSTom Eccles continue; 19621b74faddSTom Eccles } 19631b74faddSTom Eccles case fir::LowerIntrinsicArgAs::Box: { 19645c428079SSlava Zakharin hlfir::Entity actual = arg.value()->getActual(loc, builder); 19651b74faddSTom Eccles auto [exv, cleanup] = genOptionalBox(builder, loc, actual, isPresent); 19661b74faddSTom Eccles addToCleanups(std::move(cleanup)); 19671b74faddSTom Eccles operands.emplace_back(exv); 19681b74faddSTom Eccles continue; 19691b74faddSTom Eccles } 19701b74faddSTom Eccles case fir::LowerIntrinsicArgAs::Inquired: { 19715c428079SSlava Zakharin hlfir::Entity actual = arg.value()->getActual(loc, builder); 19721b74faddSTom Eccles auto [exv, cleanup] = 19731b74faddSTom Eccles hlfir::translateToExtendedValue(loc, builder, actual); 19741b74faddSTom Eccles addToCleanups(std::move(cleanup)); 19751b74faddSTom Eccles operands.emplace_back(exv); 19761b74faddSTom Eccles continue; 19771b74faddSTom Eccles } 19781b74faddSTom Eccles } 19791b74faddSTom Eccles llvm_unreachable("bad switch"); 19801b74faddSTom Eccles } 19815c428079SSlava Zakharin 19825c428079SSlava Zakharin hlfir::Entity actual = arg.value()->getActual(loc, builder); 1983199e4974SJean Perier switch (argRules.lowerAs) { 19846dcb31deSTom Eccles case fir::LowerIntrinsicArgAs::Value: 1985199e4974SJean Perier operands.emplace_back( 1986199e4974SJean Perier Fortran::lower::convertToValue(loc, converter, actual, stmtCtx)); 1987199e4974SJean Perier continue; 19886dcb31deSTom Eccles case fir::LowerIntrinsicArgAs::Addr: 1989ff2912a0SJean Perier operands.emplace_back(Fortran::lower::convertToAddress( 1990ff2912a0SJean Perier loc, converter, actual, stmtCtx, getActualFortranElementType())); 1991199e4974SJean Perier continue; 19926dcb31deSTom Eccles case fir::LowerIntrinsicArgAs::Box: 1993ff2912a0SJean Perier operands.emplace_back(Fortran::lower::convertToBox( 1994ff2912a0SJean Perier loc, converter, actual, stmtCtx, getActualFortranElementType())); 1995199e4974SJean Perier continue; 19966dcb31deSTom Eccles case fir::LowerIntrinsicArgAs::Inquired: 1997498f706bSSlava Zakharin if (const Fortran::lower::SomeExpr *expr = 1998498f706bSSlava Zakharin callContext.procRef.UnwrapArgExpr(arg.index())) { 1999498f706bSSlava Zakharin if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>( 2000498f706bSSlava Zakharin *expr)) { 2001498f706bSSlava Zakharin // NULL() pointer without a MOLD must be passed as a deallocated 2002498f706bSSlava Zakharin // pointer (see table 16.5 in Fortran 2018 standard). 2003498f706bSSlava Zakharin // !fir.box<!fir.ptr<none>> should always be valid in this context. 2004498f706bSSlava Zakharin mlir::Type noneTy = mlir::NoneType::get(builder.getContext()); 2005498f706bSSlava Zakharin mlir::Type nullPtrTy = fir::PointerType::get(noneTy); 2006498f706bSSlava Zakharin mlir::Type boxTy = fir::BoxType::get(nullPtrTy); 2007498f706bSSlava Zakharin mlir::Value boxStorage = 2008498f706bSSlava Zakharin fir::factory::genNullBoxStorage(builder, loc, boxTy); 2009498f706bSSlava Zakharin hlfir::EntityWithAttributes nullBoxEntity = 2010498f706bSSlava Zakharin extendedValueToHlfirEntity(loc, builder, boxStorage, 2011498f706bSSlava Zakharin ".tmp.null_box"); 2012498f706bSSlava Zakharin operands.emplace_back(Fortran::lower::translateToExtendedValue( 2013498f706bSSlava Zakharin loc, builder, nullBoxEntity, stmtCtx)); 2014498f706bSSlava Zakharin continue; 2015498f706bSSlava Zakharin } 2016498f706bSSlava Zakharin } 2017efd3d875SJean Perier // Place hlfir.expr in memory, and unbox fir.boxchar. Other entities 2018efd3d875SJean Perier // are translated to fir::ExtendedValue without transformation (notably, 2019efd3d875SJean Perier // pointers/allocatable are not dereferenced). 2020efd3d875SJean Perier // TODO: once lowering to FIR retires, UBOUND and LBOUND can be simplified 2021efd3d875SJean Perier // since the fir.box lowered here are now guaranteed to contain the local 2022efd3d875SJean Perier // lower bounds thanks to the hlfir.declare (the extra rebox can be 2023efd3d875SJean Perier // removed). 2024efd3d875SJean Perier operands.emplace_back(Fortran::lower::translateToExtendedValue( 2025efd3d875SJean Perier loc, builder, actual, stmtCtx)); 2026199e4974SJean Perier continue; 2027199e4974SJean Perier } 2028199e4974SJean Perier llvm_unreachable("bad switch"); 2029199e4974SJean Perier } 2030c0b45fefSJean Perier // genIntrinsicCall needs the scalar type, even if this is a transformational 2031c0b45fefSJean Perier // procedure returning an array. 2032c0b45fefSJean Perier std::optional<mlir::Type> scalarResultType; 2033c0b45fefSJean Perier if (callContext.resultType) 2034c0b45fefSJean Perier scalarResultType = hlfir::getFortranElementType(*callContext.resultType); 20356ed4a8b9SJean Perier const std::string intrinsicName = callContext.getProcedureName(); 2036199e4974SJean Perier // Let the intrinsic library lower the intrinsic procedure call. 20370b3f9d85SDaniel Chen auto [resultExv, mustBeFreed] = genIntrinsicCall( 2038727ecaf7SjeanPerier builder, loc, intrinsicEntry, scalarResultType, operands, &converter); 20391b74faddSTom Eccles for (const hlfir::CleanupFunction &fn : cleanupFns) 20401b74faddSTom Eccles fn(); 2041c3645de2SJean Perier if (!fir::getBase(resultExv)) 2042c3645de2SJean Perier return std::nullopt; 2043c0b45fefSJean Perier hlfir::EntityWithAttributes resultEntity = extendedValueToHlfirEntity( 2044c0b45fefSJean Perier loc, builder, resultExv, ".tmp.intrinsic_result"); 2045c0b45fefSJean Perier // Move result into memory into an hlfir.expr since they are immutable from 204690cf1014SJean Perier // that point, and the result storage is some temp. "Null" is special: it 204790cf1014SJean Perier // returns a null pointer variable that should not be transformed into a value 204890cf1014SJean Perier // (what matters is the memory address). 204990cf1014SJean Perier if (resultEntity.isVariable() && intrinsicName != "null") { 2050a19e5aedSjeanPerier assert(!fir::isa_trivial(fir::unwrapRefType(resultEntity.getType())) && 2051a19e5aedSjeanPerier "expect intrinsic scalar results to not be in memory"); 2052da78ae46SJean Perier hlfir::AsExprOp asExpr; 2053da78ae46SJean Perier // Character/Derived MERGE lowering returns one of its argument address 2054da78ae46SJean Perier // (this is the only intrinsic implemented in that way so far). The 2055da78ae46SJean Perier // ownership of this address cannot be taken here since it may not be a 2056da78ae46SJean Perier // temp. 20576ed4a8b9SJean Perier if (intrinsicName == "merge") 2058da78ae46SJean Perier asExpr = builder.create<hlfir::AsExprOp>(loc, resultEntity); 2059da78ae46SJean Perier else 2060da78ae46SJean Perier asExpr = builder.create<hlfir::AsExprOp>( 2061da78ae46SJean Perier loc, resultEntity, builder.createBool(loc, mustBeFreed)); 2062da78ae46SJean Perier resultEntity = hlfir::EntityWithAttributes{asExpr.getResult()}; 2063da78ae46SJean Perier } 2064c0b45fefSJean Perier return resultEntity; 2065199e4974SJean Perier } 2066199e4974SJean Perier 20679facbb69STom Eccles /// Lower calls to intrinsic procedures with actual arguments that have been 20689facbb69STom Eccles /// pre-lowered but have not yet been prepared according to the interface. 2069d2d21301STom Eccles static std::optional<hlfir::EntityWithAttributes> genHLFIRIntrinsicRefCore( 2070d2d21301STom Eccles Fortran::lower::PreparedActualArguments &loweredActuals, 20716ed4a8b9SJean Perier const Fortran::evaluate::SpecificIntrinsic *intrinsic, 2072727ecaf7SjeanPerier const fir::IntrinsicHandlerEntry &intrinsicEntry, 20739facbb69STom Eccles CallContext &callContext) { 2074727ecaf7SjeanPerier // Try lowering transformational intrinsic ops to HLFIR ops if enabled 2075727ecaf7SjeanPerier // (transformational always have a result type) 2076727ecaf7SjeanPerier if (useHlfirIntrinsicOps && callContext.resultType) { 207791cbc3f2STom Eccles fir::FirOpBuilder &builder = callContext.getBuilder(); 207891cbc3f2STom Eccles mlir::Location loc = callContext.loc; 2079508d49a5SJacob Crawley const std::string intrinsicName = callContext.getProcedureName(); 2080727ecaf7SjeanPerier const fir::IntrinsicArgumentLoweringRules *argLowering = 2081727ecaf7SjeanPerier intrinsicEntry.getArgumentLoweringRules(); 2082d2d21301STom Eccles std::optional<hlfir::EntityWithAttributes> res = 2083d2d21301STom Eccles Fortran::lower::lowerHlfirIntrinsic(builder, loc, intrinsicName, 2084d2d21301STom Eccles loweredActuals, argLowering, 2085d2d21301STom Eccles *callContext.resultType); 2086d2d21301STom Eccles if (res) 2087d2d21301STom Eccles return res; 208891cbc3f2STom Eccles } 20899facbb69STom Eccles 20909facbb69STom Eccles // fallback to calling the intrinsic via fir.call 2091727ecaf7SjeanPerier return genIntrinsicRefCore(loweredActuals, intrinsic, intrinsicEntry, 20929facbb69STom Eccles callContext); 20939facbb69STom Eccles } 20949facbb69STom Eccles 2095199e4974SJean Perier namespace { 2096199e4974SJean Perier template <typename ElementalCallBuilderImpl> 2097199e4974SJean Perier class ElementalCallBuilder { 2098199e4974SJean Perier public: 2099199e4974SJean Perier std::optional<hlfir::EntityWithAttributes> 2100d2d21301STom Eccles genElementalCall(Fortran::lower::PreparedActualArguments &loweredActuals, 2101d2d21301STom Eccles bool isImpure, CallContext &callContext) { 21023909b60aSJean Perier mlir::Location loc = callContext.loc; 21033909b60aSJean Perier fir::FirOpBuilder &builder = callContext.getBuilder(); 21044203b062SJean Perier unsigned numArgs = loweredActuals.size(); 21054203b062SJean Perier // Step 1: dereference pointers/allocatables and compute elemental shape. 21064203b062SJean Perier mlir::Value shape; 2107d2d21301STom Eccles Fortran::lower::PreparedActualArgument *optionalWithShape; 21084203b062SJean Perier // 10.1.4 p5. Impure elemental procedures must be called in element order. 21094203b062SJean Perier bool mustBeOrdered = isImpure; 21104203b062SJean Perier for (unsigned i = 0; i < numArgs; ++i) { 21114203b062SJean Perier auto &preparedActual = loweredActuals[i]; 21124203b062SJean Perier if (preparedActual) { 21134203b062SJean Perier // Elemental procedure dummy arguments cannot be pointer/allocatables 21144203b062SJean Perier // (C15100), so it is safe to dereference any pointer or allocatable 21154203b062SJean Perier // actual argument now instead of doing this inside the elemental 21164203b062SJean Perier // region. 21178c2ed5ccSjeanPerier preparedActual->derefPointersAndAllocatables(loc, builder); 21184203b062SJean Perier // Better to load scalars outside of the loop when possible. 211987cd6f93SJean Perier if (!preparedActual->handleDynamicOptional() && 2120199e4974SJean Perier impl().canLoadActualArgumentBeforeLoop(i)) 21218c2ed5ccSjeanPerier preparedActual->loadTrivialScalar(loc, builder); 21224203b062SJean Perier // TODO: merge shape instead of using the first one. 21238c2ed5ccSjeanPerier if (!shape && preparedActual->isArray()) { 212487cd6f93SJean Perier if (preparedActual->handleDynamicOptional()) 212587cd6f93SJean Perier optionalWithShape = &*preparedActual; 212687cd6f93SJean Perier else 21278c2ed5ccSjeanPerier shape = preparedActual->genShape(loc, builder); 21284203b062SJean Perier } 21294203b062SJean Perier // 15.8.3 p1. Elemental procedure with intent(out)/intent(inout) 21304203b062SJean Perier // arguments must be called in element order. 2131199e4974SJean Perier if (impl().argMayBeModifiedByCall(i)) 21324203b062SJean Perier mustBeOrdered = true; 21334203b062SJean Perier } 21344203b062SJean Perier } 213587cd6f93SJean Perier if (!shape && optionalWithShape) { 213687cd6f93SJean Perier // If all array operands appear in optional positions, then none of them 213787cd6f93SJean Perier // is allowed to be absent as per 15.5.2.12 point 3. (6). Just pick the 213887cd6f93SJean Perier // first operand. 21398c2ed5ccSjeanPerier shape = optionalWithShape->genShape(loc, builder); 214087cd6f93SJean Perier // TODO: There is an opportunity to add a runtime check here that 214187cd6f93SJean Perier // this array is present as required. Also, the optionality of all actual 214287cd6f93SJean Perier // could be checked and reset given the Fortran requirement. 214387cd6f93SJean Perier optionalWithShape->resetOptionalAspect(); 214487cd6f93SJean Perier } 21454203b062SJean Perier assert(shape && 21464203b062SJean Perier "elemental array calls must have at least one array arguments"); 2147b5fa9af4SSlava Zakharin 2148b5fa9af4SSlava Zakharin // Evaluate the actual argument array expressions before the elemental 2149b5fa9af4SSlava Zakharin // call of an impure subprogram or a subprogram with intent(out) or 2150b5fa9af4SSlava Zakharin // intent(inout) arguments. Note that the scalar arguments are handled 2151b5fa9af4SSlava Zakharin // above. 2152b5fa9af4SSlava Zakharin if (mustBeOrdered) { 21538c2ed5ccSjeanPerier for (auto &preparedActual : loweredActuals) { 2154b5fa9af4SSlava Zakharin if (preparedActual) { 21558c2ed5ccSjeanPerier if (hlfir::AssociateOp associate = 21568c2ed5ccSjeanPerier preparedActual->associateIfArrayExpr(loc, builder)) { 2157b5fa9af4SSlava Zakharin fir::FirOpBuilder *bldr = &builder; 2158b5fa9af4SSlava Zakharin callContext.stmtCtx.attachCleanup( 2159b5fa9af4SSlava Zakharin [=]() { bldr->create<hlfir::EndAssociateOp>(loc, associate); }); 2160b5fa9af4SSlava Zakharin } 2161b5fa9af4SSlava Zakharin } 2162b5fa9af4SSlava Zakharin } 2163b5fa9af4SSlava Zakharin } 2164b5fa9af4SSlava Zakharin 2165199e4974SJean Perier // Push a new local scope so that any temps made inside the elemental 2166199e4974SJean Perier // iterations are cleaned up inside the iterations. 21673909b60aSJean Perier if (!callContext.resultType) { 21684203b062SJean Perier // Subroutine case. Generate call inside loop nest. 21695983b8b6SSlava Zakharin hlfir::LoopNest loopNest = 21705983b8b6SSlava Zakharin hlfir::genLoopNest(loc, builder, shape, !mustBeOrdered); 21715d0c5c59SJean Perier mlir::ValueRange oneBasedIndices = loopNest.oneBasedIndices; 21724203b062SJean Perier auto insPt = builder.saveInsertionPoint(); 21738bb21ae6SIvan R. Ivanov builder.setInsertionPointToStart(loopNest.body); 2174c0b45fefSJean Perier callContext.stmtCtx.pushScope(); 21754203b062SJean Perier for (auto &preparedActual : loweredActuals) 21764203b062SJean Perier if (preparedActual) 217787cd6f93SJean Perier preparedActual->setElementalIndices(oneBasedIndices); 2178199e4974SJean Perier impl().genElementalKernel(loweredActuals, callContext); 2179199e4974SJean Perier callContext.stmtCtx.finalizeAndPop(); 21804203b062SJean Perier builder.restoreInsertionPoint(insPt); 21814203b062SJean Perier return std::nullopt; 21824203b062SJean Perier } 21834203b062SJean Perier // Function case: generate call inside hlfir.elemental 21843909b60aSJean Perier mlir::Type elementType = 21853909b60aSJean Perier hlfir::getFortranElementType(*callContext.resultType); 21864203b062SJean Perier // Get result length parameters. 21874203b062SJean Perier llvm::SmallVector<mlir::Value> typeParams; 2188fac349a1SChristian Sigg if (mlir::isa<fir::CharacterType>(elementType) || 2189da78ae46SJean Perier fir::isRecordWithTypeParameters(elementType)) { 2190fac349a1SChristian Sigg auto charType = mlir::dyn_cast<fir::CharacterType>(elementType); 2191da78ae46SJean Perier if (charType && charType.hasConstantLen()) 2192da78ae46SJean Perier typeParams.push_back(builder.createIntegerConstant( 2193da78ae46SJean Perier loc, builder.getIndexType(), charType.getLen())); 2194da78ae46SJean Perier else if (charType) 2195da78ae46SJean Perier typeParams.push_back(impl().computeDynamicCharacterResultLength( 2196da78ae46SJean Perier loweredActuals, callContext)); 2197da78ae46SJean Perier else 2198da78ae46SJean Perier TODO( 2199da78ae46SJean Perier loc, 2200da78ae46SJean Perier "compute elemental PDT function result length parameters in HLFIR"); 2201da78ae46SJean Perier } 22024203b062SJean Perier auto genKernel = [&](mlir::Location l, fir::FirOpBuilder &b, 22034203b062SJean Perier mlir::ValueRange oneBasedIndices) -> hlfir::Entity { 2204c0b45fefSJean Perier callContext.stmtCtx.pushScope(); 22054203b062SJean Perier for (auto &preparedActual : loweredActuals) 22064203b062SJean Perier if (preparedActual) 220787cd6f93SJean Perier preparedActual->setElementalIndices(oneBasedIndices); 2208199e4974SJean Perier auto res = *impl().genElementalKernel(loweredActuals, callContext); 2209199e4974SJean Perier callContext.stmtCtx.finalizeAndPop(); 2210c0b45fefSJean Perier // Note that an hlfir.destroy is not emitted for the result since it 2211c0b45fefSJean Perier // is still used by the hlfir.yield_element that also marks its last 2212c0b45fefSJean Perier // use. 2213199e4974SJean Perier return res; 22144203b062SJean Perier }; 22157c9d3d5cSSlava Zakharin mlir::Value polymorphicMold; 22167c9d3d5cSSlava Zakharin if (fir::isPolymorphicType(*callContext.resultType)) 22177c9d3d5cSSlava Zakharin polymorphicMold = 22187c9d3d5cSSlava Zakharin impl().getPolymorphicResultMold(loweredActuals, callContext); 22197b4aa95dSSlava Zakharin mlir::Value elemental = 22207b4aa95dSSlava Zakharin hlfir::genElementalOp(loc, builder, elementType, shape, typeParams, 22217c9d3d5cSSlava Zakharin genKernel, !mustBeOrdered, polymorphicMold); 2222ab1db262SSlava Zakharin // If the function result requires finalization, then it has to be done 2223ab1db262SSlava Zakharin // for the array result of the elemental call. We have to communicate 2224ab1db262SSlava Zakharin // this via the DestroyOp's attribute. 2225ab1db262SSlava Zakharin bool mustFinalizeExpr = impl().resultMayRequireFinalization(callContext); 2226c0b45fefSJean Perier fir::FirOpBuilder *bldr = &builder; 2227ab1db262SSlava Zakharin callContext.stmtCtx.attachCleanup([=]() { 2228ab1db262SSlava Zakharin bldr->create<hlfir::DestroyOp>(loc, elemental, mustFinalizeExpr); 2229ab1db262SSlava Zakharin }); 2230c0b45fefSJean Perier return hlfir::EntityWithAttributes{elemental}; 22314203b062SJean Perier } 22324203b062SJean Perier 2233199e4974SJean Perier private: 2234199e4974SJean Perier ElementalCallBuilderImpl &impl() { 2235199e4974SJean Perier return *static_cast<ElementalCallBuilderImpl *>(this); 2236199e4974SJean Perier } 2237199e4974SJean Perier }; 2238199e4974SJean Perier 2239199e4974SJean Perier class ElementalUserCallBuilder 2240199e4974SJean Perier : public ElementalCallBuilder<ElementalUserCallBuilder> { 2241199e4974SJean Perier public: 2242199e4974SJean Perier ElementalUserCallBuilder(Fortran::lower::CallerInterface &caller, 2243199e4974SJean Perier mlir::FunctionType callSiteType) 2244199e4974SJean Perier : caller{caller}, callSiteType{callSiteType} {} 2245199e4974SJean Perier std::optional<hlfir::Entity> 2246d2d21301STom Eccles genElementalKernel(Fortran::lower::PreparedActualArguments &loweredActuals, 2247199e4974SJean Perier CallContext &callContext) { 2248199e4974SJean Perier return genUserCall(loweredActuals, caller, callSiteType, callContext); 2249199e4974SJean Perier } 2250199e4974SJean Perier 2251199e4974SJean Perier bool argMayBeModifiedByCall(unsigned argIdx) const { 2252199e4974SJean Perier assert(argIdx < caller.getPassedArguments().size() && "bad argument index"); 2253199e4974SJean Perier return caller.getPassedArguments()[argIdx].mayBeModifiedByCall(); 2254199e4974SJean Perier } 2255199e4974SJean Perier 2256199e4974SJean Perier bool canLoadActualArgumentBeforeLoop(unsigned argIdx) const { 2257199e4974SJean Perier using PassBy = Fortran::lower::CallerInterface::PassEntityBy; 225845760be3SPeter Klausler const auto &passedArgs{caller.getPassedArguments()}; 225945760be3SPeter Klausler assert(argIdx < passedArgs.size() && "bad argument index"); 2260199e4974SJean Perier // If the actual argument does not need to be passed via an address, 2261199e4974SJean Perier // or will be passed in the address of a temporary copy, it can be loaded 2262199e4974SJean Perier // before the elemental loop nest. 226345760be3SPeter Klausler const auto &arg{passedArgs[argIdx]}; 2264199e4974SJean Perier return arg.passBy == PassBy::Value || 2265199e4974SJean Perier arg.passBy == PassBy::BaseAddressValueAttribute; 2266199e4974SJean Perier } 2267199e4974SJean Perier 2268d2d21301STom Eccles mlir::Value computeDynamicCharacterResultLength( 2269d2d21301STom Eccles Fortran::lower::PreparedActualArguments &loweredActuals, 2270da78ae46SJean Perier CallContext &callContext) { 2271c232137dSValentin Clement (バレンタイン クレメン) fir::FirOpBuilder &builder = callContext.getBuilder(); 2272c232137dSValentin Clement (バレンタイン クレメン) mlir::Location loc = callContext.loc; 2273c232137dSValentin Clement (バレンタイン クレメン) auto &converter = callContext.converter; 2274c232137dSValentin Clement (バレンタイン クレメン) mlir::Type idxTy = builder.getIndexType(); 2275c232137dSValentin Clement (バレンタイン クレメン) llvm::SmallVector<CallCleanUp> callCleanUps; 2276c232137dSValentin Clement (バレンタイン クレメン) 2277c232137dSValentin Clement (バレンタイン クレメン) prepareUserCallArguments(loweredActuals, caller, callSiteType, callContext, 2278c232137dSValentin Clement (バレンタイン クレメン) callCleanUps); 2279c232137dSValentin Clement (バレンタイン クレメン) 2280c232137dSValentin Clement (バレンタイン クレメン) callContext.symMap.pushScope(); 2281c232137dSValentin Clement (バレンタイン クレメン) 2282c232137dSValentin Clement (バレンタイン クレメン) // Map prepared argument to dummy symbol to be able to lower spec expr. 2283c232137dSValentin Clement (バレンタイン クレメン) for (const auto &arg : caller.getPassedArguments()) { 2284c232137dSValentin Clement (バレンタイン クレメン) const Fortran::semantics::Symbol *sym = caller.getDummySymbol(arg); 2285c232137dSValentin Clement (バレンタイン クレメン) assert(sym && "expect symbol for dummy argument"); 2286c232137dSValentin Clement (バレンタイン クレメン) auto input = caller.getInput(arg); 2287c232137dSValentin Clement (バレンタイン クレメン) fir::ExtendedValue exv = Fortran::lower::translateToExtendedValue( 2288c232137dSValentin Clement (バレンタイン クレメン) loc, builder, hlfir::Entity{input}, callContext.stmtCtx); 2289c232137dSValentin Clement (バレンタイン クレメン) fir::FortranVariableOpInterface variableIface = hlfir::genDeclare( 2290c232137dSValentin Clement (バレンタイン クレメン) loc, builder, exv, "dummy.tmp", fir::FortranVariableFlagsAttr{}); 2291c232137dSValentin Clement (バレンタイン クレメン) callContext.symMap.addVariableDefinition(*sym, variableIface); 2292c232137dSValentin Clement (バレンタイン クレメン) } 2293c232137dSValentin Clement (バレンタイン クレメン) 2294c232137dSValentin Clement (バレンタイン クレメン) auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value { 2295c232137dSValentin Clement (バレンタイン クレメン) mlir::Value convertExpr = builder.createConvert( 2296c232137dSValentin Clement (バレンタイン クレメン) loc, idxTy, 2297c232137dSValentin Clement (バレンタイン クレメン) fir::getBase(converter.genExprValue(expr, callContext.stmtCtx))); 2298c232137dSValentin Clement (バレンタイン クレメン) return fir::factory::genMaxWithZero(builder, loc, convertExpr); 2299c232137dSValentin Clement (バレンタイン クレメン) }; 2300c232137dSValentin Clement (バレンタイン クレメン) 2301c232137dSValentin Clement (バレンタイン クレメン) llvm::SmallVector<mlir::Value> lengths; 2302c232137dSValentin Clement (バレンタイン クレメン) caller.walkResultLengths( 2303c232137dSValentin Clement (バレンタイン クレメン) [&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) { 2304c232137dSValentin Clement (バレンタイン クレメン) assert(!isAssumedSizeExtent && "result cannot be assumed-size"); 2305c232137dSValentin Clement (バレンタイン クレメン) lengths.emplace_back(lowerSpecExpr(e)); 2306c232137dSValentin Clement (バレンタイン クレメン) }); 2307c232137dSValentin Clement (バレンタイン クレメン) callContext.symMap.popScope(); 2308c232137dSValentin Clement (バレンタイン クレメン) assert(lengths.size() == 1 && "expect 1 length parameter for the result"); 2309c232137dSValentin Clement (バレンタイン クレメン) return lengths[0]; 2310da78ae46SJean Perier } 2311da78ae46SJean Perier 23127c9d3d5cSSlava Zakharin mlir::Value getPolymorphicResultMold( 23137c9d3d5cSSlava Zakharin Fortran::lower::PreparedActualArguments &loweredActuals, 23147c9d3d5cSSlava Zakharin CallContext &callContext) { 23157c9d3d5cSSlava Zakharin fir::emitFatalError(callContext.loc, 23167c9d3d5cSSlava Zakharin "elemental function call with polymorphic result"); 23177c9d3d5cSSlava Zakharin return {}; 23187c9d3d5cSSlava Zakharin } 23197c9d3d5cSSlava Zakharin 2320ab1db262SSlava Zakharin bool resultMayRequireFinalization(CallContext &callContext) const { 2321ab1db262SSlava Zakharin std::optional<Fortran::evaluate::DynamicType> retTy = 2322ab1db262SSlava Zakharin caller.getCallDescription().proc().GetType(); 2323ab1db262SSlava Zakharin if (!retTy) 2324ab1db262SSlava Zakharin return false; 2325ab1db262SSlava Zakharin 2326ab1db262SSlava Zakharin if (retTy->IsPolymorphic() || retTy->IsUnlimitedPolymorphic()) 2327ab1db262SSlava Zakharin fir::emitFatalError( 2328ab1db262SSlava Zakharin callContext.loc, 2329ab1db262SSlava Zakharin "elemental function call with [unlimited-]polymorphic result"); 2330ab1db262SSlava Zakharin 2331ab1db262SSlava Zakharin if (retTy->category() == Fortran::common::TypeCategory::Derived) { 2332ab1db262SSlava Zakharin const Fortran::semantics::DerivedTypeSpec &typeSpec = 2333ab1db262SSlava Zakharin retTy->GetDerivedTypeSpec(); 2334ab1db262SSlava Zakharin return Fortran::semantics::IsFinalizable(typeSpec); 2335ab1db262SSlava Zakharin } 2336ab1db262SSlava Zakharin 2337ab1db262SSlava Zakharin return false; 2338ab1db262SSlava Zakharin } 2339ab1db262SSlava Zakharin 2340199e4974SJean Perier private: 2341199e4974SJean Perier Fortran::lower::CallerInterface &caller; 2342199e4974SJean Perier mlir::FunctionType callSiteType; 2343199e4974SJean Perier }; 2344199e4974SJean Perier 2345199e4974SJean Perier class ElementalIntrinsicCallBuilder 2346199e4974SJean Perier : public ElementalCallBuilder<ElementalIntrinsicCallBuilder> { 2347199e4974SJean Perier public: 2348199e4974SJean Perier ElementalIntrinsicCallBuilder( 23496ed4a8b9SJean Perier const Fortran::evaluate::SpecificIntrinsic *intrinsic, 2350727ecaf7SjeanPerier const fir::IntrinsicHandlerEntry &intrinsicEntry, bool isFunction) 2351727ecaf7SjeanPerier : intrinsic{intrinsic}, intrinsicEntry{intrinsicEntry}, 2352727ecaf7SjeanPerier isFunction{isFunction} {} 2353199e4974SJean Perier std::optional<hlfir::Entity> 2354d2d21301STom Eccles genElementalKernel(Fortran::lower::PreparedActualArguments &loweredActuals, 2355199e4974SJean Perier CallContext &callContext) { 2356727ecaf7SjeanPerier return genHLFIRIntrinsicRefCore(loweredActuals, intrinsic, intrinsicEntry, 2357c0b45fefSJean Perier callContext); 2358199e4974SJean Perier } 2359199e4974SJean Perier // Elemental intrinsic functions cannot modify their arguments. 2360199e4974SJean Perier bool argMayBeModifiedByCall(int) const { return !isFunction; } 2361199e4974SJean Perier bool canLoadActualArgumentBeforeLoop(int) const { 2362199e4974SJean Perier // Elemental intrinsic functions never need the actual addresses 2363199e4974SJean Perier // of their arguments. 2364199e4974SJean Perier return isFunction; 2365199e4974SJean Perier } 2366199e4974SJean Perier 2367d2d21301STom Eccles mlir::Value computeDynamicCharacterResultLength( 2368d2d21301STom Eccles Fortran::lower::PreparedActualArguments &loweredActuals, 2369da78ae46SJean Perier CallContext &callContext) { 23706ed4a8b9SJean Perier if (intrinsic) 23716ed4a8b9SJean Perier if (intrinsic->name == "adjustr" || intrinsic->name == "adjustl" || 23726ed4a8b9SJean Perier intrinsic->name == "merge") 23738c2ed5ccSjeanPerier return loweredActuals[0].value().genCharLength( 23748c2ed5ccSjeanPerier callContext.loc, callContext.getBuilder()); 2375da78ae46SJean Perier // Character MIN/MAX is the min/max of the arguments length that are 2376da78ae46SJean Perier // present. 2377da78ae46SJean Perier TODO(callContext.loc, 2378da78ae46SJean Perier "compute elemental character min/max function result length in HLFIR"); 2379da78ae46SJean Perier } 2380da78ae46SJean Perier 23817c9d3d5cSSlava Zakharin mlir::Value getPolymorphicResultMold( 23827c9d3d5cSSlava Zakharin Fortran::lower::PreparedActualArguments &loweredActuals, 23837c9d3d5cSSlava Zakharin CallContext &callContext) { 23847c9d3d5cSSlava Zakharin if (!intrinsic) 23857c9d3d5cSSlava Zakharin return {}; 23867c9d3d5cSSlava Zakharin 23877c9d3d5cSSlava Zakharin if (intrinsic->name == "merge") { 23887c9d3d5cSSlava Zakharin // MERGE seems to be the only elemental function that can produce 23897c9d3d5cSSlava Zakharin // polymorphic result. The MERGE's result is polymorphic iff 23907c9d3d5cSSlava Zakharin // both TSOURCE and FSOURCE are polymorphic, and they also must have 23917c9d3d5cSSlava Zakharin // the same declared and dynamic types. So any of them can be used 23927c9d3d5cSSlava Zakharin // for the mold. 23937c9d3d5cSSlava Zakharin assert(!loweredActuals.empty()); 23948c2ed5ccSjeanPerier return loweredActuals.front()->getPolymorphicMold(callContext.loc); 23957c9d3d5cSSlava Zakharin } 23967c9d3d5cSSlava Zakharin 23977c9d3d5cSSlava Zakharin return {}; 23987c9d3d5cSSlava Zakharin } 23997c9d3d5cSSlava Zakharin 2400ab1db262SSlava Zakharin bool resultMayRequireFinalization( 2401ab1db262SSlava Zakharin [[maybe_unused]] CallContext &callContext) const { 2402ab1db262SSlava Zakharin // FIXME: need access to the CallerInterface's return type 2403ab1db262SSlava Zakharin // to check if the result may need finalization (e.g. the result 2404ab1db262SSlava Zakharin // of MERGE). 2405ab1db262SSlava Zakharin return false; 2406ab1db262SSlava Zakharin } 2407ab1db262SSlava Zakharin 2408199e4974SJean Perier private: 24096ed4a8b9SJean Perier const Fortran::evaluate::SpecificIntrinsic *intrinsic; 2410727ecaf7SjeanPerier fir::IntrinsicHandlerEntry intrinsicEntry; 2411199e4974SJean Perier const bool isFunction; 2412199e4974SJean Perier }; 2413199e4974SJean Perier } // namespace 2414199e4974SJean Perier 241587cd6f93SJean Perier static std::optional<mlir::Value> 241687cd6f93SJean Perier genIsPresentIfArgMaybeAbsent(mlir::Location loc, hlfir::Entity actual, 241787cd6f93SJean Perier const Fortran::lower::SomeExpr &expr, 241887cd6f93SJean Perier CallContext &callContext, 241987cd6f93SJean Perier bool passAsAllocatableOrPointer) { 2420f025e411SPeter Klausler if (!Fortran::evaluate::MayBePassedAsAbsentOptional(expr)) 242187cd6f93SJean Perier return std::nullopt; 242287cd6f93SJean Perier fir::FirOpBuilder &builder = callContext.getBuilder(); 242387cd6f93SJean Perier if (!passAsAllocatableOrPointer && 2424f025e411SPeter Klausler Fortran::evaluate::IsAllocatableOrPointerObject(expr)) { 242587cd6f93SJean Perier // Passing Allocatable/Pointer to non-pointer/non-allocatable OPTIONAL. 242687cd6f93SJean Perier // Fortran 2018 15.5.2.12 point 1: If unallocated/disassociated, it is 242787cd6f93SJean Perier // as if the argument was absent. The main care here is to not do a 242887cd6f93SJean Perier // copy-in/copy-out because the temp address, even though pointing to a 242987cd6f93SJean Perier // null size storage, would not be a nullptr and therefore the argument 243087cd6f93SJean Perier // would not be considered absent on the callee side. Note: if the 243187cd6f93SJean Perier // allocatable/pointer is also optional, it cannot be absent as per 243287cd6f93SJean Perier // 15.5.2.12 point 7. and 8. We rely on this to un-conditionally read 243387cd6f93SJean Perier // the allocatable/pointer descriptor here. 243487cd6f93SJean Perier mlir::Value addr = genVariableRawAddress(loc, builder, actual); 243587cd6f93SJean Perier return builder.genIsNotNullAddr(loc, addr); 243687cd6f93SJean Perier } 243787cd6f93SJean Perier // TODO: what if passing allocatable target to optional intent(in) pointer? 243887cd6f93SJean Perier // May fall into the category above if the allocatable is not optional. 243987cd6f93SJean Perier 244087cd6f93SJean Perier // Passing an optional to an optional. 244187cd6f93SJean Perier return builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), actual) 244287cd6f93SJean Perier .getResult(); 244387cd6f93SJean Perier } 244487cd6f93SJean Perier 244509880ef6STom Eccles // Lower a reference to an elemental intrinsic procedure with array arguments 244609880ef6STom Eccles // and custom optional handling 244709880ef6STom Eccles static std::optional<hlfir::EntityWithAttributes> 244809880ef6STom Eccles genCustomElementalIntrinsicRef( 244909880ef6STom Eccles const Fortran::evaluate::SpecificIntrinsic *intrinsic, 245009880ef6STom Eccles CallContext &callContext) { 245109880ef6STom Eccles assert(callContext.isElementalProcWithArrayArgs() && 245209880ef6STom Eccles "Use genCustomIntrinsicRef for scalar calls"); 245309880ef6STom Eccles mlir::Location loc = callContext.loc; 245409880ef6STom Eccles auto &converter = callContext.converter; 245509880ef6STom Eccles Fortran::lower::PreparedActualArguments operands; 245609880ef6STom Eccles assert(intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling( 245709880ef6STom Eccles callContext.procRef, *intrinsic, converter)); 245809880ef6STom Eccles 245909880ef6STom Eccles // callback for optional arguments 246009880ef6STom Eccles auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) { 246109880ef6STom Eccles hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR( 246209880ef6STom Eccles loc, converter, expr, callContext.symMap, callContext.stmtCtx); 246309880ef6STom Eccles std::optional<mlir::Value> isPresent = 246409880ef6STom Eccles genIsPresentIfArgMaybeAbsent(loc, actual, expr, callContext, 246509880ef6STom Eccles /*passAsAllocatableOrPointer=*/false); 246609880ef6STom Eccles operands.emplace_back( 246709880ef6STom Eccles Fortran::lower::PreparedActualArgument{actual, isPresent}); 246809880ef6STom Eccles }; 246909880ef6STom Eccles 247009880ef6STom Eccles // callback for non-optional arguments 247109880ef6STom Eccles auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr, 247209880ef6STom Eccles fir::LowerIntrinsicArgAs lowerAs) { 247309880ef6STom Eccles hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR( 247409880ef6STom Eccles loc, converter, expr, callContext.symMap, callContext.stmtCtx); 247509880ef6STom Eccles operands.emplace_back(Fortran::lower::PreparedActualArgument{ 247609880ef6STom Eccles actual, /*isPresent=*/std::nullopt}); 247709880ef6STom Eccles }; 247809880ef6STom Eccles 247909880ef6STom Eccles Fortran::lower::prepareCustomIntrinsicArgument( 248009880ef6STom Eccles callContext.procRef, *intrinsic, callContext.resultType, 248109880ef6STom Eccles prepareOptionalArg, prepareOtherArg, converter); 248209880ef6STom Eccles 2483727ecaf7SjeanPerier std::optional<fir::IntrinsicHandlerEntry> intrinsicEntry = 2484727ecaf7SjeanPerier fir::lookupIntrinsicHandler(callContext.getBuilder(), 2485727ecaf7SjeanPerier callContext.getProcedureName(), 2486727ecaf7SjeanPerier callContext.resultType); 2487727ecaf7SjeanPerier assert(intrinsicEntry.has_value() && 2488727ecaf7SjeanPerier "intrinsic with custom handling for OPTIONAL arguments must have " 2489727ecaf7SjeanPerier "lowering entries"); 249009880ef6STom Eccles // All of the custom intrinsic elementals with custom handling are pure 249109880ef6STom Eccles // functions 2492727ecaf7SjeanPerier return ElementalIntrinsicCallBuilder{intrinsic, *intrinsicEntry, 249309880ef6STom Eccles /*isFunction=*/true} 249409880ef6STom Eccles .genElementalCall(operands, /*isImpure=*/false, callContext); 249509880ef6STom Eccles } 249609880ef6STom Eccles 2497b0935fc4STom Eccles // Lower a reference to an intrinsic procedure with custom optional handling 2498b0935fc4STom Eccles static std::optional<hlfir::EntityWithAttributes> 2499b0935fc4STom Eccles genCustomIntrinsicRef(const Fortran::evaluate::SpecificIntrinsic *intrinsic, 2500b0935fc4STom Eccles CallContext &callContext) { 250109880ef6STom Eccles assert(!callContext.isElementalProcWithArrayArgs() && 250209880ef6STom Eccles "Needs to be run through ElementalIntrinsicCallBuilder first"); 2503b0935fc4STom Eccles mlir::Location loc = callContext.loc; 2504b0935fc4STom Eccles fir::FirOpBuilder &builder = callContext.getBuilder(); 2505b0935fc4STom Eccles auto &converter = callContext.converter; 2506b0935fc4STom Eccles auto &stmtCtx = callContext.stmtCtx; 2507b0935fc4STom Eccles assert(intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling( 2508b0935fc4STom Eccles callContext.procRef, *intrinsic, converter)); 2509b0935fc4STom Eccles Fortran::lower::PreparedActualArguments loweredActuals; 2510b0935fc4STom Eccles 2511b0935fc4STom Eccles // callback for optional arguments 2512b0935fc4STom Eccles auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) { 2513b0935fc4STom Eccles hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR( 2514b0935fc4STom Eccles loc, converter, expr, callContext.symMap, callContext.stmtCtx); 2515b0935fc4STom Eccles mlir::Value isPresent = 2516b0935fc4STom Eccles genIsPresentIfArgMaybeAbsent(loc, actual, expr, callContext, 2517b0935fc4STom Eccles /*passAsAllocatableOrPointer*/ false) 2518b0935fc4STom Eccles .value(); 2519b0935fc4STom Eccles loweredActuals.emplace_back( 2520b0935fc4STom Eccles Fortran::lower::PreparedActualArgument{actual, {isPresent}}); 2521b0935fc4STom Eccles }; 2522b0935fc4STom Eccles 2523b0935fc4STom Eccles // callback for non-optional arguments 2524b0935fc4STom Eccles auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr, 2525b0935fc4STom Eccles fir::LowerIntrinsicArgAs lowerAs) { 2526b0935fc4STom Eccles auto getActualFortranElementType = [&]() -> mlir::Type { 2527b0935fc4STom Eccles return hlfir::getFortranElementType(converter.genType(expr)); 2528b0935fc4STom Eccles }; 2529b0935fc4STom Eccles hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR( 2530b0935fc4STom Eccles loc, converter, expr, callContext.symMap, callContext.stmtCtx); 2531b0935fc4STom Eccles std::optional<fir::ExtendedValue> exv; 2532b0935fc4STom Eccles switch (lowerAs) { 2533b0935fc4STom Eccles case fir::LowerIntrinsicArgAs::Value: 2534b0935fc4STom Eccles exv = Fortran::lower::convertToValue(loc, converter, actual, stmtCtx); 2535b0935fc4STom Eccles break; 2536b0935fc4STom Eccles case fir::LowerIntrinsicArgAs::Addr: 2537b0935fc4STom Eccles exv = Fortran::lower::convertToAddress(loc, converter, actual, stmtCtx, 2538b0935fc4STom Eccles getActualFortranElementType()); 2539b0935fc4STom Eccles break; 2540b0935fc4STom Eccles case fir::LowerIntrinsicArgAs::Box: 2541b0935fc4STom Eccles exv = Fortran::lower::convertToBox(loc, converter, actual, stmtCtx, 2542b0935fc4STom Eccles getActualFortranElementType()); 2543b0935fc4STom Eccles break; 2544b0935fc4STom Eccles case fir::LowerIntrinsicArgAs::Inquired: 2545f55622f0SValentin Clement (バレンタイン クレメン) exv = Fortran::lower::translateToExtendedValue(loc, builder, actual, 2546f55622f0SValentin Clement (バレンタイン クレメン) stmtCtx); 2547f55622f0SValentin Clement (バレンタイン クレメン) break; 2548b0935fc4STom Eccles } 2549b0935fc4STom Eccles if (!exv) 2550b0935fc4STom Eccles llvm_unreachable("bad switch"); 2551b0935fc4STom Eccles actual = extendedValueToHlfirEntity(loc, builder, exv.value(), 2552b0935fc4STom Eccles "tmp.custom_intrinsic_arg"); 2553b0935fc4STom Eccles loweredActuals.emplace_back(Fortran::lower::PreparedActualArgument{ 2554b0935fc4STom Eccles actual, /*isPresent=*/std::nullopt}); 2555b0935fc4STom Eccles }; 2556b0935fc4STom Eccles 2557b0935fc4STom Eccles Fortran::lower::prepareCustomIntrinsicArgument( 2558b0935fc4STom Eccles callContext.procRef, *intrinsic, callContext.resultType, 2559b0935fc4STom Eccles prepareOptionalArg, prepareOtherArg, converter); 2560b0935fc4STom Eccles 2561db7b665cSTom Eccles return genCustomIntrinsicRefCore(loweredActuals, intrinsic, callContext); 2562b0935fc4STom Eccles } 2563b0935fc4STom Eccles 25643909b60aSJean Perier /// Lower an intrinsic procedure reference. 25656ed4a8b9SJean Perier /// \p intrinsic is null if this is an intrinsic module procedure that must be 25666ed4a8b9SJean Perier /// lowered as if it were an intrinsic module procedure (like C_LOC which is a 25676ed4a8b9SJean Perier /// procedure from intrinsic module iso_c_binding). Otherwise, \p intrinsic 25686ed4a8b9SJean Perier /// must not be null. 2569727ecaf7SjeanPerier 2570c3645de2SJean Perier static std::optional<hlfir::EntityWithAttributes> 25716ed4a8b9SJean Perier genIntrinsicRef(const Fortran::evaluate::SpecificIntrinsic *intrinsic, 2572727ecaf7SjeanPerier const fir::IntrinsicHandlerEntry &intrinsicEntry, 25733909b60aSJean Perier CallContext &callContext) { 25743909b60aSJean Perier mlir::Location loc = callContext.loc; 2575d2d21301STom Eccles Fortran::lower::PreparedActualArguments loweredActuals; 25766dcb31deSTom Eccles const fir::IntrinsicArgumentLoweringRules *argLowering = 2577727ecaf7SjeanPerier intrinsicEntry.getArgumentLoweringRules(); 25783909b60aSJean Perier for (const auto &arg : llvm::enumerate(callContext.procRef.arguments())) { 257932983aa0SJean Perier 258032983aa0SJean Perier if (!arg.value()) { 258132983aa0SJean Perier // Absent optional. 258232983aa0SJean Perier loweredActuals.push_back(std::nullopt); 258332983aa0SJean Perier continue; 258432983aa0SJean Perier } 2585b013ebe0SJean Perier auto *expr = 2586b013ebe0SJean Perier Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg.value()); 2587b013ebe0SJean Perier if (!expr) { 258832983aa0SJean Perier // TYPE(*) dummy. They are only allowed as argument of a few intrinsics 258932983aa0SJean Perier // that do not take optional arguments: see Fortran 2018 standard C710. 259032983aa0SJean Perier const Fortran::evaluate::Symbol *assumedTypeSym = 259132983aa0SJean Perier arg.value()->GetAssumedTypeDummy(); 259232983aa0SJean Perier if (!assumedTypeSym) 259332983aa0SJean Perier fir::emitFatalError(loc, 259432983aa0SJean Perier "expected assumed-type symbol as actual argument"); 259532983aa0SJean Perier std::optional<fir::FortranVariableOpInterface> var = 259632983aa0SJean Perier callContext.symMap.lookupVariableDefinition(*assumedTypeSym); 259732983aa0SJean Perier if (!var) 259832983aa0SJean Perier fir::emitFatalError(loc, "assumed-type symbol was not lowered"); 259932983aa0SJean Perier assert( 260032983aa0SJean Perier (!argLowering || 260132983aa0SJean Perier !fir::lowerIntrinsicArgumentAs(*argLowering, arg.index()) 260232983aa0SJean Perier .handleDynamicOptional) && 260332983aa0SJean Perier "TYPE(*) are not expected to appear as optional intrinsic arguments"); 2604d2d21301STom Eccles loweredActuals.push_back(Fortran::lower::PreparedActualArgument{ 260532983aa0SJean Perier hlfir::Entity{*var}, /*isPresent=*/std::nullopt}); 2606b013ebe0SJean Perier continue; 2607b013ebe0SJean Perier } 2608b91a25efSYusuke MINATO // arguments of bitwise comparison functions may not have nsw flag 2609b91a25efSYusuke MINATO // even if -fno-wrapv is enabled 2610b91a25efSYusuke MINATO mlir::arith::IntegerOverflowFlags iofBackup{}; 2611b91a25efSYusuke MINATO auto isBitwiseComparison = [](const std::string intrinsicName) -> bool { 2612b91a25efSYusuke MINATO if (intrinsicName == "bge" || intrinsicName == "bgt" || 2613b91a25efSYusuke MINATO intrinsicName == "ble" || intrinsicName == "blt") 2614b91a25efSYusuke MINATO return true; 2615b91a25efSYusuke MINATO return false; 2616b91a25efSYusuke MINATO }; 2617b91a25efSYusuke MINATO if (isBitwiseComparison(callContext.getProcedureName())) { 2618b91a25efSYusuke MINATO iofBackup = callContext.getBuilder().getIntegerOverflowFlags(); 2619b91a25efSYusuke MINATO callContext.getBuilder().setIntegerOverflowFlags( 2620b91a25efSYusuke MINATO mlir::arith::IntegerOverflowFlags::none); 2621b91a25efSYusuke MINATO } 2622199e4974SJean Perier auto loweredActual = Fortran::lower::convertExprToHLFIR( 2623199e4974SJean Perier loc, callContext.converter, *expr, callContext.symMap, 2624199e4974SJean Perier callContext.stmtCtx); 2625b91a25efSYusuke MINATO if (isBitwiseComparison(callContext.getProcedureName())) 2626b91a25efSYusuke MINATO callContext.getBuilder().setIntegerOverflowFlags(iofBackup); 2627b91a25efSYusuke MINATO 262887cd6f93SJean Perier std::optional<mlir::Value> isPresent; 2629199e4974SJean Perier if (argLowering) { 26306dcb31deSTom Eccles fir::ArgLoweringRule argRules = 26316dcb31deSTom Eccles fir::lowerIntrinsicArgumentAs(*argLowering, arg.index()); 263287cd6f93SJean Perier if (argRules.handleDynamicOptional) 263387cd6f93SJean Perier isPresent = 263487cd6f93SJean Perier genIsPresentIfArgMaybeAbsent(loc, loweredActual, *expr, callContext, 263587cd6f93SJean Perier /*passAsAllocatableOrPointer=*/false); 2636b013ebe0SJean Perier } 2637d2d21301STom Eccles loweredActuals.push_back( 2638d2d21301STom Eccles Fortran::lower::PreparedActualArgument{loweredActual, isPresent}); 2639b013ebe0SJean Perier } 2640199e4974SJean Perier 2641199e4974SJean Perier if (callContext.isElementalProcWithArrayArgs()) { 2642199e4974SJean Perier // All intrinsic elemental functions are pure. 2643199e4974SJean Perier const bool isFunction = callContext.resultType.has_value(); 2644727ecaf7SjeanPerier return ElementalIntrinsicCallBuilder{intrinsic, intrinsicEntry, isFunction} 26451538ad9fSTom Eccles .genElementalCall(loweredActuals, /*isImpure=*/!isFunction, 26461538ad9fSTom Eccles callContext); 2647199e4974SJean Perier } 26489facbb69STom Eccles std::optional<hlfir::EntityWithAttributes> result = genHLFIRIntrinsicRefCore( 2649727ecaf7SjeanPerier loweredActuals, intrinsic, intrinsicEntry, callContext); 2650fac349a1SChristian Sigg if (result && mlir::isa<hlfir::ExprType>(result->getType())) { 2651c0b45fefSJean Perier fir::FirOpBuilder *bldr = &callContext.getBuilder(); 2652c0b45fefSJean Perier callContext.stmtCtx.attachCleanup( 2653c3645de2SJean Perier [=]() { bldr->create<hlfir::DestroyOp>(loc, *result); }); 2654c0b45fefSJean Perier } 2655c0b45fefSJean Perier return result; 2656b013ebe0SJean Perier } 2657b013ebe0SJean Perier 2658727ecaf7SjeanPerier static std::optional<hlfir::EntityWithAttributes> 2659727ecaf7SjeanPerier genIntrinsicRef(const Fortran::evaluate::SpecificIntrinsic *intrinsic, 2660727ecaf7SjeanPerier CallContext &callContext) { 2661727ecaf7SjeanPerier mlir::Location loc = callContext.loc; 2662727ecaf7SjeanPerier auto &converter = callContext.converter; 2663727ecaf7SjeanPerier if (intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling( 2664727ecaf7SjeanPerier callContext.procRef, *intrinsic, converter)) { 2665727ecaf7SjeanPerier if (callContext.isElementalProcWithArrayArgs()) 2666727ecaf7SjeanPerier return genCustomElementalIntrinsicRef(intrinsic, callContext); 2667727ecaf7SjeanPerier return genCustomIntrinsicRef(intrinsic, callContext); 2668727ecaf7SjeanPerier } 2669727ecaf7SjeanPerier std::optional<fir::IntrinsicHandlerEntry> intrinsicEntry = 2670727ecaf7SjeanPerier fir::lookupIntrinsicHandler(callContext.getBuilder(), 2671727ecaf7SjeanPerier callContext.getProcedureName(), 2672727ecaf7SjeanPerier callContext.resultType); 2673727ecaf7SjeanPerier if (!intrinsicEntry) 2674727ecaf7SjeanPerier fir::crashOnMissingIntrinsic(loc, callContext.getProcedureName()); 2675727ecaf7SjeanPerier return genIntrinsicRef(intrinsic, *intrinsicEntry, callContext); 2676727ecaf7SjeanPerier } 2677727ecaf7SjeanPerier 26783909b60aSJean Perier /// Main entry point to lower procedure references, regardless of what they are. 26793909b60aSJean Perier static std::optional<hlfir::EntityWithAttributes> 26803909b60aSJean Perier genProcedureRef(CallContext &callContext) { 26813909b60aSJean Perier mlir::Location loc = callContext.loc; 2682727ecaf7SjeanPerier fir::FirOpBuilder &builder = callContext.getBuilder(); 26833909b60aSJean Perier if (auto *intrinsic = callContext.procRef.proc().GetSpecificIntrinsic()) 26846ed4a8b9SJean Perier return genIntrinsicRef(intrinsic, callContext); 2685727ecaf7SjeanPerier // Intercept non BIND(C) module procedure reference that have lowering 2686727ecaf7SjeanPerier // handlers defined for there name. Otherwise, lower them as user 2687727ecaf7SjeanPerier // procedure calls and expect the implementation to be part of 2688727ecaf7SjeanPerier // runtime libraries with the proper name mangling. 26893e32b809SRazvan Lupusoru if (Fortran::lower::isIntrinsicModuleProcRef(callContext.procRef) && 2690a49f630cSjeanPerier !callContext.isBindcCall()) 2691727ecaf7SjeanPerier if (std::optional<fir::IntrinsicHandlerEntry> intrinsicEntry = 2692727ecaf7SjeanPerier fir::lookupIntrinsicHandler(builder, callContext.getProcedureName(), 2693727ecaf7SjeanPerier callContext.resultType)) 2694727ecaf7SjeanPerier return genIntrinsicRef(nullptr, *intrinsicEntry, callContext); 26953909b60aSJean Perier 26963909b60aSJean Perier if (callContext.isStatementFunctionCall()) 26973909b60aSJean Perier return genStmtFunctionRef(loc, callContext.converter, callContext.symMap, 26983909b60aSJean Perier callContext.stmtCtx, callContext.procRef); 26993909b60aSJean Perier 27003909b60aSJean Perier Fortran::lower::CallerInterface caller(callContext.procRef, 27013909b60aSJean Perier callContext.converter); 27023909b60aSJean Perier mlir::FunctionType callSiteType = caller.genFunctionType(); 27038c2ed5ccSjeanPerier const bool isElemental = callContext.isElementalProcWithArrayArgs(); 2704d2d21301STom Eccles Fortran::lower::PreparedActualArguments loweredActuals; 27053909b60aSJean Perier // Lower the actual arguments 27063909b60aSJean Perier for (const Fortran::lower::CallInterface< 27073909b60aSJean Perier Fortran::lower::CallerInterface>::PassedEntity &arg : 27083909b60aSJean Perier caller.getPassedArguments()) 27093909b60aSJean Perier if (const auto *actual = arg.entity) { 27103909b60aSJean Perier const auto *expr = actual->UnwrapExpr(); 27112984699aSjeanPerier if (!expr) { 27122984699aSjeanPerier // TYPE(*) actual argument. 27132984699aSjeanPerier const Fortran::evaluate::Symbol *assumedTypeSym = 27142984699aSjeanPerier actual->GetAssumedTypeDummy(); 27152984699aSjeanPerier if (!assumedTypeSym) 27162984699aSjeanPerier fir::emitFatalError( 27172984699aSjeanPerier loc, "expected assumed-type symbol as actual argument"); 27182984699aSjeanPerier std::optional<fir::FortranVariableOpInterface> var = 27192984699aSjeanPerier callContext.symMap.lookupVariableDefinition(*assumedTypeSym); 27202984699aSjeanPerier if (!var) 27212984699aSjeanPerier fir::emitFatalError(loc, "assumed-type symbol was not lowered"); 27222984699aSjeanPerier hlfir::Entity actual{*var}; 27232984699aSjeanPerier std::optional<mlir::Value> isPresent; 27242984699aSjeanPerier if (arg.isOptional()) { 27252984699aSjeanPerier // Passing an optional TYPE(*) to an optional TYPE(*). Note that 27262984699aSjeanPerier // TYPE(*) cannot be ALLOCATABLE/POINTER (C709) so there is no 27272984699aSjeanPerier // need to cover the case of passing an ALLOCATABLE/POINTER to an 27282984699aSjeanPerier // OPTIONAL. 27292984699aSjeanPerier isPresent = 27302984699aSjeanPerier builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), actual) 27312984699aSjeanPerier .getResult(); 27322984699aSjeanPerier } 27332984699aSjeanPerier loweredActuals.push_back(Fortran::lower::PreparedActualArgument{ 27342984699aSjeanPerier hlfir::Entity{*var}, isPresent}); 27352984699aSjeanPerier continue; 27362984699aSjeanPerier } 27372984699aSjeanPerier 273887cd6f93SJean Perier if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>( 273987cd6f93SJean Perier *expr)) { 2740af09219eSDaniel Chen if ((arg.passBy != 2741af09219eSDaniel Chen Fortran::lower::CallerInterface::PassEntityBy::MutableBox) && 2742af09219eSDaniel Chen (arg.passBy != 2743af09219eSDaniel Chen Fortran::lower::CallerInterface::PassEntityBy::BoxProcRef)) { 274487cd6f93SJean Perier assert( 27453909b60aSJean Perier arg.isOptional() && 274687cd6f93SJean Perier "NULL must be passed only to pointer, allocatable, or OPTIONAL"); 274787cd6f93SJean Perier // Trying to lower NULL() outside of any context would lead to 274887cd6f93SJean Perier // trouble. NULL() here is equivalent to not providing the 274987cd6f93SJean Perier // actual argument. 275087cd6f93SJean Perier loweredActuals.emplace_back(std::nullopt); 275187cd6f93SJean Perier continue; 275287cd6f93SJean Perier } 275387cd6f93SJean Perier } 275487cd6f93SJean Perier 27558c2ed5ccSjeanPerier if (isElemental && !arg.hasValueAttribute() && 27568c2ed5ccSjeanPerier Fortran::evaluate::IsVariable(*expr) && 27578c2ed5ccSjeanPerier Fortran::evaluate::HasVectorSubscript(*expr)) { 27588c2ed5ccSjeanPerier // Vector subscripted arguments are copied in calls, except in elemental 27598c2ed5ccSjeanPerier // calls without VALUE attribute where Fortran 2018 15.5.2.4 point 21 27608c2ed5ccSjeanPerier // does not apply and the address of each element must be passed. 27618c2ed5ccSjeanPerier hlfir::ElementalAddrOp elementalAddr = 27628c2ed5ccSjeanPerier Fortran::lower::convertVectorSubscriptedExprToElementalAddr( 27638c2ed5ccSjeanPerier loc, callContext.converter, *expr, callContext.symMap, 27648c2ed5ccSjeanPerier callContext.stmtCtx); 27658c2ed5ccSjeanPerier loweredActuals.emplace_back( 27668c2ed5ccSjeanPerier Fortran::lower::PreparedActualArgument{elementalAddr}); 27678c2ed5ccSjeanPerier continue; 27688c2ed5ccSjeanPerier } 27698c2ed5ccSjeanPerier 27703909b60aSJean Perier auto loweredActual = Fortran::lower::convertExprToHLFIR( 27713909b60aSJean Perier loc, callContext.converter, *expr, callContext.symMap, 27723909b60aSJean Perier callContext.stmtCtx); 277387cd6f93SJean Perier std::optional<mlir::Value> isPresent; 277487cd6f93SJean Perier if (arg.isOptional()) 277587cd6f93SJean Perier isPresent = genIsPresentIfArgMaybeAbsent( 277687cd6f93SJean Perier loc, loweredActual, *expr, callContext, 277787cd6f93SJean Perier arg.passBy == 277887cd6f93SJean Perier Fortran::lower::CallerInterface::PassEntityBy::MutableBox); 277987cd6f93SJean Perier 27803909b60aSJean Perier loweredActuals.emplace_back( 2781d2d21301STom Eccles Fortran::lower::PreparedActualArgument{loweredActual, isPresent}); 27823909b60aSJean Perier } else { 27833909b60aSJean Perier // Optional dummy argument for which there is no actual argument. 27843909b60aSJean Perier loweredActuals.emplace_back(std::nullopt); 2785b013ebe0SJean Perier } 27868c2ed5ccSjeanPerier if (isElemental) { 27873909b60aSJean Perier bool isImpure = false; 27883909b60aSJean Perier if (const Fortran::semantics::Symbol *procSym = 27893909b60aSJean Perier callContext.procRef.proc().GetSymbol()) 27903909b60aSJean Perier isImpure = !Fortran::semantics::IsPureProcedure(*procSym); 2791199e4974SJean Perier return ElementalUserCallBuilder{caller, callSiteType}.genElementalCall( 2792199e4974SJean Perier loweredActuals, isImpure, callContext); 27933909b60aSJean Perier } 27943909b60aSJean Perier return genUserCall(loweredActuals, caller, callSiteType, callContext); 27953909b60aSJean Perier } 2796e78e4a17SJean Perier 27978c2ed5ccSjeanPerier hlfir::Entity Fortran::lower::PreparedActualArgument::getActual( 27988c2ed5ccSjeanPerier mlir::Location loc, fir::FirOpBuilder &builder) const { 27998c2ed5ccSjeanPerier if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual)) { 28008c2ed5ccSjeanPerier if (oneBasedElementalIndices) 28018c2ed5ccSjeanPerier return hlfir::getElementAt(loc, builder, *actualEntity, 28028c2ed5ccSjeanPerier *oneBasedElementalIndices); 28038c2ed5ccSjeanPerier return *actualEntity; 28048c2ed5ccSjeanPerier } 28058c2ed5ccSjeanPerier assert(oneBasedElementalIndices && "expect elemental context"); 28068c2ed5ccSjeanPerier hlfir::ElementalAddrOp elementalAddr = 28078c2ed5ccSjeanPerier std::get<hlfir::ElementalAddrOp>(actual); 28088c2ed5ccSjeanPerier mlir::IRMapping mapper; 28098c2ed5ccSjeanPerier auto alwaysFalse = [](hlfir::ElementalOp) -> bool { return false; }; 28108c2ed5ccSjeanPerier mlir::Value addr = hlfir::inlineElementalOp( 28118c2ed5ccSjeanPerier loc, builder, elementalAddr, *oneBasedElementalIndices, mapper, 28128c2ed5ccSjeanPerier /*mustRecursivelyInline=*/alwaysFalse); 28138c2ed5ccSjeanPerier assert(elementalAddr.getCleanup().empty() && "no clean-up expected"); 28148c2ed5ccSjeanPerier elementalAddr.erase(); 28158c2ed5ccSjeanPerier return hlfir::Entity{addr}; 28168c2ed5ccSjeanPerier } 28178c2ed5ccSjeanPerier 28186ed4a8b9SJean Perier bool Fortran::lower::isIntrinsicModuleProcRef( 28196ed4a8b9SJean Perier const Fortran::evaluate::ProcedureRef &procRef) { 28206ed4a8b9SJean Perier const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol(); 28216ed4a8b9SJean Perier if (!symbol) 28226ed4a8b9SJean Perier return false; 28236ed4a8b9SJean Perier const Fortran::semantics::Symbol *module = 28246ed4a8b9SJean Perier symbol->GetUltimate().owner().GetSymbol(); 28253e32b809SRazvan Lupusoru return module && module->attrs().test(Fortran::semantics::Attr::INTRINSIC); 28266ed4a8b9SJean Perier } 28276ed4a8b9SJean Perier 2828c7c5666aSjeanPerier static bool isInWhereMaskedExpression(fir::FirOpBuilder &builder) { 2829c7c5666aSjeanPerier // The MASK of the outer WHERE is not masked itself. 2830c7c5666aSjeanPerier mlir::Operation *op = builder.getRegion().getParentOp(); 2831c7c5666aSjeanPerier return op && op->getParentOfType<hlfir::WhereOp>(); 2832c7c5666aSjeanPerier } 2833c7c5666aSjeanPerier 2834c0921586SKazu Hirata std::optional<hlfir::EntityWithAttributes> Fortran::lower::convertCallToHLFIR( 2835e78e4a17SJean Perier mlir::Location loc, Fortran::lower::AbstractConverter &converter, 2836c0921586SKazu Hirata const evaluate::ProcedureRef &procRef, std::optional<mlir::Type> resultType, 2837c0921586SKazu Hirata Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { 2838c7c5666aSjeanPerier auto &builder = converter.getFirOpBuilder(); 2839c7c5666aSjeanPerier if (resultType && !procRef.IsElemental() && 2840c7c5666aSjeanPerier isInWhereMaskedExpression(builder) && 2841c7c5666aSjeanPerier !builder.getRegion().getParentOfType<hlfir::ExactlyOnceOp>()) { 2842c7c5666aSjeanPerier // Non elemental calls inside a where-assignment-stmt must be executed 2843c7c5666aSjeanPerier // exactly once without mask control. Lower them in a special region so that 2844c7c5666aSjeanPerier // this can be enforced whenscheduling forall/where expression evaluations. 2845c7c5666aSjeanPerier Fortran::lower::StatementContext localStmtCtx; 2846c7c5666aSjeanPerier mlir::Type bogusType = builder.getIndexType(); 2847c7c5666aSjeanPerier auto exactlyOnce = builder.create<hlfir::ExactlyOnceOp>(loc, bogusType); 2848c7c5666aSjeanPerier mlir::Block *block = builder.createBlock(&exactlyOnce.getBody()); 2849c7c5666aSjeanPerier builder.setInsertionPointToStart(block); 2850c7c5666aSjeanPerier CallContext callContext(procRef, resultType, loc, converter, symMap, 2851c7c5666aSjeanPerier localStmtCtx); 2852c7c5666aSjeanPerier std::optional<hlfir::EntityWithAttributes> res = 2853c7c5666aSjeanPerier genProcedureRef(callContext); 2854c7c5666aSjeanPerier assert(res.has_value() && "must be a function"); 2855c7c5666aSjeanPerier auto yield = builder.create<hlfir::YieldOp>(loc, *res); 2856c7c5666aSjeanPerier Fortran::lower::genCleanUpInRegionIfAny(loc, builder, yield.getCleanup(), 2857c7c5666aSjeanPerier localStmtCtx); 2858c7c5666aSjeanPerier builder.setInsertionPointAfter(exactlyOnce); 2859c7c5666aSjeanPerier exactlyOnce->getResult(0).setType(res->getType()); 2860c7c5666aSjeanPerier if (hlfir::isFortranValue(exactlyOnce.getResult())) 2861c7c5666aSjeanPerier return hlfir::EntityWithAttributes{exactlyOnce.getResult()}; 2862c7c5666aSjeanPerier // Create hlfir.declare for the result to satisfy 2863c7c5666aSjeanPerier // hlfir::EntityWithAttributes requirements. 2864c7c5666aSjeanPerier auto [exv, cleanup] = hlfir::translateToExtendedValue( 2865c7c5666aSjeanPerier loc, builder, hlfir::Entity{exactlyOnce}); 2866c7c5666aSjeanPerier assert(!cleanup && "resut is a variable"); 2867c7c5666aSjeanPerier return hlfir::genDeclare(loc, builder, exv, ".func.pointer.result", 2868c7c5666aSjeanPerier fir::FortranVariableFlagsAttr{}); 2869c7c5666aSjeanPerier } 28703909b60aSJean Perier CallContext callContext(procRef, resultType, loc, converter, symMap, stmtCtx); 28713909b60aSJean Perier return genProcedureRef(callContext); 2872e78e4a17SJean Perier } 287367169233SJean Perier 287467169233SJean Perier void Fortran::lower::convertUserDefinedAssignmentToHLFIR( 287567169233SJean Perier mlir::Location loc, Fortran::lower::AbstractConverter &converter, 287667169233SJean Perier const evaluate::ProcedureRef &procRef, hlfir::Entity lhs, hlfir::Entity rhs, 287767169233SJean Perier Fortran::lower::SymMap &symMap) { 287867169233SJean Perier Fortran::lower::StatementContext definedAssignmentContext; 287967169233SJean Perier CallContext callContext(procRef, /*resultType=*/std::nullopt, loc, converter, 288067169233SJean Perier symMap, definedAssignmentContext); 288167169233SJean Perier Fortran::lower::CallerInterface caller(procRef, converter); 288267169233SJean Perier mlir::FunctionType callSiteType = caller.genFunctionType(); 288367169233SJean Perier PreparedActualArgument preparedLhs{lhs, /*isPresent=*/std::nullopt}; 288467169233SJean Perier PreparedActualArgument preparedRhs{rhs, /*isPresent=*/std::nullopt}; 288567169233SJean Perier PreparedActualArguments loweredActuals{preparedLhs, preparedRhs}; 288667169233SJean Perier genUserCall(loweredActuals, caller, callSiteType, callContext); 288767169233SJean Perier return; 288867169233SJean Perier } 2889