xref: /llvm-project/flang/lib/Lower/ConvertCall.cpp (revision cd7e65398fbbd9642573013800dc3ae1e7307f82)
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