xref: /llvm-project/flang/lib/Lower/ConvertCall.cpp (revision cd7e65398fbbd9642573013800dc3ae1e7307f82)
1 //===-- ConvertCall.cpp ---------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "flang/Lower/ConvertCall.h"
14 #include "flang/Lower/Allocatable.h"
15 #include "flang/Lower/ConvertExprToHLFIR.h"
16 #include "flang/Lower/ConvertProcedureDesignator.h"
17 #include "flang/Lower/ConvertVariable.h"
18 #include "flang/Lower/CustomIntrinsicCall.h"
19 #include "flang/Lower/HlfirIntrinsics.h"
20 #include "flang/Lower/StatementContext.h"
21 #include "flang/Lower/SymbolMap.h"
22 #include "flang/Optimizer/Builder/BoxValue.h"
23 #include "flang/Optimizer/Builder/Character.h"
24 #include "flang/Optimizer/Builder/FIRBuilder.h"
25 #include "flang/Optimizer/Builder/HLFIRTools.h"
26 #include "flang/Optimizer/Builder/IntrinsicCall.h"
27 #include "flang/Optimizer/Builder/LowLevelIntrinsics.h"
28 #include "flang/Optimizer/Builder/MutableBox.h"
29 #include "flang/Optimizer/Builder/Runtime/Derived.h"
30 #include "flang/Optimizer/Builder/Todo.h"
31 #include "flang/Optimizer/Dialect/CUF/CUFOps.h"
32 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
33 #include "flang/Optimizer/HLFIR/HLFIROps.h"
34 #include "mlir/IR/IRMapping.h"
35 #include "llvm/Support/CommandLine.h"
36 #include "llvm/Support/Debug.h"
37 #include <optional>
38 
39 #define DEBUG_TYPE "flang-lower-expr"
40 
41 static llvm::cl::opt<bool> useHlfirIntrinsicOps(
42     "use-hlfir-intrinsic-ops", llvm::cl::init(true),
43     llvm::cl::desc("Lower via HLFIR transformational intrinsic operations such "
44                    "as hlfir.sum"));
45 
46 static constexpr char tempResultName[] = ".tmp.func_result";
47 
48 /// Helper to package a Value and its properties into an ExtendedValue.
49 static fir::ExtendedValue toExtendedValue(mlir::Location loc, mlir::Value base,
50                                           llvm::ArrayRef<mlir::Value> extents,
51                                           llvm::ArrayRef<mlir::Value> lengths) {
52   mlir::Type type = base.getType();
53   if (mlir::isa<fir::BaseBoxType>(type))
54     return fir::BoxValue(base, /*lbounds=*/{}, lengths, extents);
55   type = fir::unwrapRefType(type);
56   if (mlir::isa<fir::BaseBoxType>(type))
57     return fir::MutableBoxValue(base, lengths, /*mutableProperties*/ {});
58   if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(type)) {
59     if (seqTy.getDimension() != extents.size())
60       fir::emitFatalError(loc, "incorrect number of extents for array");
61     if (mlir::isa<fir::CharacterType>(seqTy.getEleTy())) {
62       if (lengths.empty())
63         fir::emitFatalError(loc, "missing length for character");
64       assert(lengths.size() == 1);
65       return fir::CharArrayBoxValue(base, lengths[0], extents);
66     }
67     return fir::ArrayBoxValue(base, extents);
68   }
69   if (mlir::isa<fir::CharacterType>(type)) {
70     if (lengths.empty())
71       fir::emitFatalError(loc, "missing length for character");
72     assert(lengths.size() == 1);
73     return fir::CharBoxValue(base, lengths[0]);
74   }
75   return base;
76 }
77 
78 /// Lower a type(C_PTR/C_FUNPTR) argument with VALUE attribute into a
79 /// reference. A C pointer can correspond to a Fortran dummy argument of type
80 /// C_PTR with the VALUE attribute. (see 18.3.6 note 3).
81 static mlir::Value genRecordCPtrValueArg(fir::FirOpBuilder &builder,
82                                          mlir::Location loc, mlir::Value rec,
83                                          mlir::Type ty) {
84   mlir::Value cAddr = fir::factory::genCPtrOrCFunptrAddr(builder, loc, rec, ty);
85   mlir::Value cVal = builder.create<fir::LoadOp>(loc, cAddr);
86   return builder.createConvert(loc, cAddr.getType(), cVal);
87 }
88 
89 // Find the argument that corresponds to the host associations.
90 // Verify some assumptions about how the signature was built here.
91 [[maybe_unused]] static unsigned findHostAssocTuplePos(mlir::func::FuncOp fn) {
92   // Scan the argument list from last to first as the host associations are
93   // appended for now.
94   for (unsigned i = fn.getNumArguments(); i > 0; --i)
95     if (fn.getArgAttr(i - 1, fir::getHostAssocAttrName())) {
96       // Host assoc tuple must be last argument (for now).
97       assert(i == fn.getNumArguments() && "tuple must be last");
98       return i - 1;
99     }
100   llvm_unreachable("anyFuncArgsHaveAttr failed");
101 }
102 
103 mlir::Value
104 Fortran::lower::argumentHostAssocs(Fortran::lower::AbstractConverter &converter,
105                                    mlir::Value arg) {
106   if (auto addr = mlir::dyn_cast_or_null<fir::AddrOfOp>(arg.getDefiningOp())) {
107     auto &builder = converter.getFirOpBuilder();
108     if (auto funcOp = builder.getNamedFunction(addr.getSymbol()))
109       if (fir::anyFuncArgsHaveAttr(funcOp, fir::getHostAssocAttrName()))
110         return converter.hostAssocTupleValue();
111   }
112   return {};
113 }
114 
115 static bool mustCastFuncOpToCopeWithImplicitInterfaceMismatch(
116     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
117     mlir::FunctionType callSiteType, mlir::FunctionType funcOpType) {
118   // Deal with argument number mismatch by making a function pointer so
119   // that function type cast can be inserted. Do not emit a warning here
120   // because this can happen in legal program if the function is not
121   // defined here and it was first passed as an argument without any more
122   // information.
123   if (callSiteType.getNumResults() != funcOpType.getNumResults() ||
124       callSiteType.getNumInputs() != funcOpType.getNumInputs())
125     return true;
126 
127   // Implicit interface result type mismatch are not standard Fortran, but
128   // some compilers are not complaining about it.  The front end is not
129   // protecting lowering from this currently. Support this with a
130   // discouraging warning.
131   // Cast the actual function to the current caller implicit type because
132   // that is the behavior we would get if we could not see the definition.
133   if (callSiteType.getResults() != funcOpType.getResults()) {
134     LLVM_DEBUG(mlir::emitWarning(
135         loc, "a return type mismatch is not standard compliant and may "
136              "lead to undefined behavior."));
137     return true;
138   }
139 
140   // In HLFIR, there is little attempt to cope with implicit interface
141   // mismatch on the arguments. The argument are always prepared according
142   // to the implicit interface. Cast the actual function if any of the
143   // argument mismatch cannot be dealt with a simple fir.convert.
144   if (converter.getLoweringOptions().getLowerToHighLevelFIR())
145     for (auto [actualType, dummyType] :
146          llvm::zip(callSiteType.getInputs(), funcOpType.getInputs()))
147       if (actualType != dummyType &&
148           !fir::ConvertOp::canBeConverted(actualType, dummyType))
149         return true;
150   return false;
151 }
152 
153 static mlir::Value readDim3Value(fir::FirOpBuilder &builder, mlir::Location loc,
154                                  mlir::Value dim3Addr, llvm::StringRef comp) {
155   mlir::Type i32Ty = builder.getI32Type();
156   mlir::Type refI32Ty = fir::ReferenceType::get(i32Ty);
157   llvm::SmallVector<mlir::Value> lenParams;
158 
159   mlir::Value designate = builder.create<hlfir::DesignateOp>(
160       loc, refI32Ty, dim3Addr, /*component=*/comp,
161       /*componentShape=*/mlir::Value{}, hlfir::DesignateOp::Subscripts{},
162       /*substring=*/mlir::ValueRange{}, /*complexPartAttr=*/std::nullopt,
163       mlir::Value{}, lenParams);
164 
165   return hlfir::loadTrivialScalar(loc, builder, hlfir::Entity{designate});
166 }
167 
168 static mlir::Value remapActualToDummyDescriptor(
169     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
170     Fortran::lower::SymMap &symMap,
171     const Fortran::lower::CallerInterface::PassedEntity &arg,
172     Fortran::lower::CallerInterface &caller, bool isBindcCall) {
173   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
174   mlir::IndexType idxTy = builder.getIndexType();
175   mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
176   Fortran::lower::StatementContext localStmtCtx;
177   auto lowerSpecExpr = [&](const auto &expr,
178                            bool isAssumedSizeExtent) -> mlir::Value {
179     mlir::Value convertExpr = builder.createConvert(
180         loc, idxTy, fir::getBase(converter.genExprValue(expr, localStmtCtx)));
181     if (isAssumedSizeExtent)
182       return convertExpr;
183     return fir::factory::genMaxWithZero(builder, loc, convertExpr);
184   };
185   bool mapSymbols = caller.mustMapInterfaceSymbolsForDummyArgument(arg);
186   if (mapSymbols) {
187     symMap.pushScope();
188     const Fortran::semantics::Symbol *sym = caller.getDummySymbol(arg);
189     assert(sym && "call must have explicit interface to map interface symbols");
190     Fortran::lower::mapCallInterfaceSymbolsForDummyArgument(converter, caller,
191                                                             symMap, *sym);
192   }
193   llvm::SmallVector<mlir::Value> extents;
194   llvm::SmallVector<mlir::Value> lengths;
195   mlir::Type dummyBoxType = caller.getDummyArgumentType(arg);
196   mlir::Type dummyBaseType = fir::unwrapPassByRefType(dummyBoxType);
197   if (mlir::isa<fir::SequenceType>(dummyBaseType))
198     caller.walkDummyArgumentExtents(
199         arg, [&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) {
200           extents.emplace_back(lowerSpecExpr(e, isAssumedSizeExtent));
201         });
202   mlir::Value shape;
203   if (!extents.empty()) {
204     if (isBindcCall) {
205       // Preserve zero lower bounds (see F'2023 18.5.3).
206       llvm::SmallVector<mlir::Value> lowerBounds(extents.size(), zero);
207       shape = builder.genShape(loc, lowerBounds, extents);
208     } else {
209       shape = builder.genShape(loc, extents);
210     }
211   }
212 
213   hlfir::Entity explicitArgument = hlfir::Entity{caller.getInput(arg)};
214   mlir::Type dummyElementType = fir::unwrapSequenceType(dummyBaseType);
215   if (auto recType = llvm::dyn_cast<fir::RecordType>(dummyElementType))
216     if (recType.getNumLenParams() > 0)
217       TODO(loc, "sequence association of length parameterized derived type "
218                 "dummy arguments");
219   if (fir::isa_char(dummyElementType))
220     lengths.emplace_back(hlfir::genCharLength(loc, builder, explicitArgument));
221   mlir::Value baseAddr =
222       hlfir::genVariableRawAddress(loc, builder, explicitArgument);
223   baseAddr = builder.createConvert(loc, fir::ReferenceType::get(dummyBaseType),
224                                    baseAddr);
225   mlir::Value mold;
226   if (fir::isPolymorphicType(dummyBoxType))
227     mold = explicitArgument;
228   mlir::Value remapped =
229       builder.create<fir::EmboxOp>(loc, dummyBoxType, baseAddr, shape,
230                                    /*slice=*/mlir::Value{}, lengths, mold);
231   if (mapSymbols)
232     symMap.popScope();
233   return remapped;
234 }
235 
236 /// Create a descriptor for sequenced associated descriptor that are passed
237 /// by descriptor. Sequence association (F'2023 15.5.2.12) implies that the
238 /// dummy shape and rank need to not be the same as the actual argument. This
239 /// helper creates a descriptor based on the dummy shape and rank (sequence
240 /// association can only happen with explicit and assumed-size array) so that it
241 /// is safe to assume the rank of the incoming descriptor inside the callee.
242 /// This helper must be called once all the actual arguments have been lowered
243 /// and placed inside "caller". Copy-in/copy-out must already have been
244 /// generated if needed using the actual argument shape (the dummy shape may be
245 /// assumed-size).
246 static void remapActualToDummyDescriptors(
247     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
248     Fortran::lower::SymMap &symMap,
249     const Fortran::lower::PreparedActualArguments &loweredActuals,
250     Fortran::lower::CallerInterface &caller, bool isBindcCall) {
251   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
252   for (auto [preparedActual, arg] :
253        llvm::zip(loweredActuals, caller.getPassedArguments())) {
254     if (arg.isSequenceAssociatedDescriptor()) {
255       if (!preparedActual.value().handleDynamicOptional()) {
256         mlir::Value remapped = remapActualToDummyDescriptor(
257             loc, converter, symMap, arg, caller, isBindcCall);
258         caller.placeInput(arg, remapped);
259       } else {
260         // Absent optional actual argument descriptor cannot be read and
261         // remapped unconditionally.
262         mlir::Type dummyType = caller.getDummyArgumentType(arg);
263         mlir::Value isPresent = preparedActual.value().getIsPresent();
264         auto &argLambdaCapture = arg;
265         mlir::Value remapped =
266             builder
267                 .genIfOp(loc, {dummyType}, isPresent,
268                          /*withElseRegion=*/true)
269                 .genThen([&]() {
270                   mlir::Value newBox = remapActualToDummyDescriptor(
271                       loc, converter, symMap, argLambdaCapture, caller,
272                       isBindcCall);
273                   builder.create<fir::ResultOp>(loc, newBox);
274                 })
275                 .genElse([&]() {
276                   mlir::Value absent =
277                       builder.create<fir::AbsentOp>(loc, dummyType);
278                   builder.create<fir::ResultOp>(loc, absent);
279                 })
280                 .getResults()[0];
281         caller.placeInput(arg, remapped);
282       }
283     }
284   }
285 }
286 
287 std::pair<Fortran::lower::LoweredResult, bool>
288 Fortran::lower::genCallOpAndResult(
289     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
290     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
291     Fortran::lower::CallerInterface &caller, mlir::FunctionType callSiteType,
292     std::optional<mlir::Type> resultType, bool isElemental) {
293   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
294   using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
295   bool mustPopSymMap = false;
296   if (caller.mustMapInterfaceSymbolsForResult()) {
297     symMap.pushScope();
298     mustPopSymMap = true;
299     Fortran::lower::mapCallInterfaceSymbolsForResult(converter, caller, symMap);
300   }
301   // If this is an indirect call, retrieve the function address. Also retrieve
302   // the result length if this is a character function (note that this length
303   // will be used only if there is no explicit length in the local interface).
304   mlir::Value funcPointer;
305   mlir::Value charFuncPointerLength;
306   if (const Fortran::evaluate::ProcedureDesignator *procDesignator =
307           caller.getIfIndirectCall()) {
308     if (mlir::Value passedArg = caller.getIfPassedArg()) {
309       // Procedure pointer component call with PASS argument. To avoid
310       // "double" lowering of the ComponentRef, semantics only place the
311       // ComponentRef in the ActualArguments, not in the ProcedureDesignator (
312       // that is only the component symbol).
313       // Fetch the passed argument and addresses of its procedure pointer
314       // component.
315       funcPointer = Fortran::lower::derefPassProcPointerComponent(
316           loc, converter, *procDesignator, passedArg, symMap, stmtCtx);
317     } else {
318       Fortran::lower::SomeExpr expr{*procDesignator};
319       fir::ExtendedValue loweredProc =
320           converter.genExprAddr(loc, expr, stmtCtx);
321       funcPointer = fir::getBase(loweredProc);
322       // Dummy procedure may have assumed length, in which case the result
323       // length was passed along the dummy procedure.
324       // This is not possible with procedure pointer components.
325       if (const fir::CharBoxValue *charBox = loweredProc.getCharBox())
326         charFuncPointerLength = charBox->getLen();
327     }
328   }
329 
330   const bool isExprCall =
331       converter.getLoweringOptions().getLowerToHighLevelFIR() &&
332       callSiteType.getNumResults() == 1 &&
333       llvm::isa<fir::SequenceType>(callSiteType.getResult(0));
334 
335   mlir::IndexType idxTy = builder.getIndexType();
336   auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value {
337     mlir::Value convertExpr = builder.createConvert(
338         loc, idxTy, fir::getBase(converter.genExprValue(expr, stmtCtx)));
339     return fir::factory::genMaxWithZero(builder, loc, convertExpr);
340   };
341   llvm::SmallVector<mlir::Value> resultLengths;
342   mlir::Value arrayResultShape;
343   hlfir::EvaluateInMemoryOp evaluateInMemory;
344   auto allocatedResult = [&]() -> std::optional<fir::ExtendedValue> {
345     llvm::SmallVector<mlir::Value> extents;
346     llvm::SmallVector<mlir::Value> lengths;
347     if (!caller.callerAllocateResult())
348       return {};
349     mlir::Type type = caller.getResultStorageType();
350     if (mlir::isa<fir::SequenceType>(type))
351       caller.walkResultExtents(
352           [&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) {
353             assert(!isAssumedSizeExtent && "result cannot be assumed-size");
354             extents.emplace_back(lowerSpecExpr(e));
355           });
356     caller.walkResultLengths(
357         [&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) {
358           assert(!isAssumedSizeExtent && "result cannot be assumed-size");
359           lengths.emplace_back(lowerSpecExpr(e));
360         });
361 
362     // Result length parameters should not be provided to box storage
363     // allocation and save_results, but they are still useful information to
364     // keep in the ExtendedValue if non-deferred.
365     if (!mlir::isa<fir::BoxType>(type)) {
366       if (fir::isa_char(fir::unwrapSequenceType(type)) && lengths.empty()) {
367         // Calling an assumed length function. This is only possible if this
368         // is a call to a character dummy procedure.
369         if (!charFuncPointerLength)
370           fir::emitFatalError(loc, "failed to retrieve character function "
371                                    "length while calling it");
372         lengths.push_back(charFuncPointerLength);
373       }
374       resultLengths = lengths;
375     }
376 
377     if (!extents.empty())
378       arrayResultShape = builder.genShape(loc, extents);
379 
380     if (isExprCall) {
381       mlir::Type exprType = hlfir::getExprType(type);
382       evaluateInMemory = builder.create<hlfir::EvaluateInMemoryOp>(
383           loc, exprType, arrayResultShape, resultLengths);
384       builder.setInsertionPointToStart(&evaluateInMemory.getBody().front());
385       return toExtendedValue(loc, evaluateInMemory.getMemory(), extents,
386                              lengths);
387     }
388 
389     if ((!extents.empty() || !lengths.empty()) && !isElemental) {
390       // Note: in the elemental context, the alloca ownership inside the
391       // elemental region is implicit, and later pass in lowering (stack
392       // reclaim) fir.do_loop will be in charge of emitting any stack
393       // save/restore if needed.
394       auto *bldr = &converter.getFirOpBuilder();
395       mlir::Value sp = bldr->genStackSave(loc);
396       stmtCtx.attachCleanup(
397           [bldr, loc, sp]() { bldr->genStackRestore(loc, sp); });
398     }
399     mlir::Value temp =
400         builder.createTemporary(loc, type, ".result", extents, resultLengths);
401     return toExtendedValue(loc, temp, extents, lengths);
402   }();
403 
404   if (mustPopSymMap)
405     symMap.popScope();
406 
407   // Place allocated result
408   if (allocatedResult) {
409     if (std::optional<Fortran::lower::CallInterface<
410             Fortran::lower::CallerInterface>::PassedEntity>
411             resultArg = caller.getPassedResult()) {
412       if (resultArg->passBy == PassBy::AddressAndLength)
413         caller.placeAddressAndLengthInput(*resultArg,
414                                           fir::getBase(*allocatedResult),
415                                           fir::getLen(*allocatedResult));
416       else if (resultArg->passBy == PassBy::BaseAddress)
417         caller.placeInput(*resultArg, fir::getBase(*allocatedResult));
418       else
419         fir::emitFatalError(
420             loc, "only expect character scalar result to be passed by ref");
421     }
422   }
423 
424   // In older Fortran, procedure argument types are inferred. This may lead
425   // different view of what the function signature is in different locations.
426   // Casts are inserted as needed below to accommodate this.
427 
428   // The mlir::func::FuncOp type prevails, unless it has a different number of
429   // arguments which can happen in legal program if it was passed as a dummy
430   // procedure argument earlier with no further type information.
431   mlir::SymbolRefAttr funcSymbolAttr;
432   bool addHostAssociations = false;
433   if (!funcPointer) {
434     mlir::FunctionType funcOpType = caller.getFuncOp().getFunctionType();
435     mlir::SymbolRefAttr symbolAttr =
436         builder.getSymbolRefAttr(caller.getMangledName());
437     if (callSiteType.getNumResults() == funcOpType.getNumResults() &&
438         callSiteType.getNumInputs() + 1 == funcOpType.getNumInputs() &&
439         fir::anyFuncArgsHaveAttr(caller.getFuncOp(),
440                                  fir::getHostAssocAttrName())) {
441       // The number of arguments is off by one, and we're lowering a function
442       // with host associations. Modify call to include host associations
443       // argument by appending the value at the end of the operands.
444       assert(funcOpType.getInput(findHostAssocTuplePos(caller.getFuncOp())) ==
445              converter.hostAssocTupleValue().getType());
446       addHostAssociations = true;
447     }
448     // When this is not a call to an internal procedure (where there is a
449     // mismatch due to the extra argument, but the interface is otherwise
450     // explicit and safe), handle interface mismatch due to F77 implicit
451     // interface "abuse" with a function address cast if needed.
452     if (!addHostAssociations &&
453         mustCastFuncOpToCopeWithImplicitInterfaceMismatch(
454             loc, converter, callSiteType, funcOpType))
455       funcPointer = builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
456     else
457       funcSymbolAttr = symbolAttr;
458 
459     // Issue a warning if the procedure name conflicts with
460     // a runtime function name a call to which has been already
461     // lowered (implying that the FuncOp has been created).
462     // The behavior is undefined in this case.
463     if (caller.getFuncOp()->hasAttrOfType<mlir::UnitAttr>(
464             fir::FIROpsDialect::getFirRuntimeAttrName()))
465       LLVM_DEBUG(mlir::emitWarning(
466           loc,
467           llvm::Twine("function name '") +
468               llvm::Twine(symbolAttr.getLeafReference()) +
469               llvm::Twine("' conflicts with a runtime function name used by "
470                           "Flang - this may lead to undefined behavior")));
471   }
472 
473   mlir::FunctionType funcType =
474       funcPointer ? callSiteType : caller.getFuncOp().getFunctionType();
475   llvm::SmallVector<mlir::Value> operands;
476   // First operand of indirect call is the function pointer. Cast it to
477   // required function type for the call to handle procedures that have a
478   // compatible interface in Fortran, but that have different signatures in
479   // FIR.
480   if (funcPointer) {
481     operands.push_back(
482         mlir::isa<fir::BoxProcType>(funcPointer.getType())
483             ? builder.create<fir::BoxAddrOp>(loc, funcType, funcPointer)
484             : builder.createConvert(loc, funcType, funcPointer));
485   }
486 
487   // Deal with potential mismatches in arguments types. Passing an array to a
488   // scalar argument should for instance be tolerated here.
489   bool callingImplicitInterface = caller.canBeCalledViaImplicitInterface();
490   for (auto [fst, snd] : llvm::zip(caller.getInputs(), funcType.getInputs())) {
491     // When passing arguments to a procedure that can be called by implicit
492     // interface, allow any character actual arguments to be passed to dummy
493     // arguments of any type and vice versa.
494     mlir::Value cast;
495     auto *context = builder.getContext();
496     if (mlir::isa<fir::BoxProcType>(snd) &&
497         mlir::isa<mlir::FunctionType>(fst.getType())) {
498       auto funcTy =
499           mlir::FunctionType::get(context, std::nullopt, std::nullopt);
500       auto boxProcTy = builder.getBoxProcType(funcTy);
501       if (mlir::Value host = argumentHostAssocs(converter, fst)) {
502         cast = builder.create<fir::EmboxProcOp>(
503             loc, boxProcTy, llvm::ArrayRef<mlir::Value>{fst, host});
504       } else {
505         cast = builder.create<fir::EmboxProcOp>(loc, boxProcTy, fst);
506       }
507     } else {
508       mlir::Type fromTy = fir::unwrapRefType(fst.getType());
509       if (fir::isa_builtin_cptr_type(fromTy) &&
510           Fortran::lower::isCPtrArgByValueType(snd)) {
511         cast = genRecordCPtrValueArg(builder, loc, fst, fromTy);
512       } else if (fir::isa_derived(snd) && !fir::isa_derived(fst.getType())) {
513         // TODO: remove this TODO once the old lowering is gone.
514         TODO(loc, "derived type argument passed by value");
515       } else {
516         // With the lowering to HLFIR, box arguments have already been built
517         // according to the attributes, rank, bounds, and type they should have.
518         // Do not attempt any reboxing here that could break this.
519         bool legacyLowering =
520             !converter.getLoweringOptions().getLowerToHighLevelFIR();
521         cast = builder.convertWithSemantics(loc, snd, fst,
522                                             callingImplicitInterface,
523                                             /*allowRebox=*/legacyLowering);
524       }
525     }
526     operands.push_back(cast);
527   }
528 
529   // Add host associations as necessary.
530   if (addHostAssociations)
531     operands.push_back(converter.hostAssocTupleValue());
532 
533   mlir::Value callResult;
534   unsigned callNumResults;
535   fir::FortranProcedureFlagsEnumAttr procAttrs =
536       caller.getProcedureAttrs(builder.getContext());
537 
538   if (!caller.getCallDescription().chevrons().empty()) {
539     // A call to a CUDA kernel with the chevron syntax.
540 
541     mlir::Type i32Ty = builder.getI32Type();
542     mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1);
543 
544     mlir::Value grid_x, grid_y, grid_z;
545     if (caller.getCallDescription().chevrons()[0].GetType()->category() ==
546         Fortran::common::TypeCategory::Integer) {
547       // If grid is an integer, it is converted to dim3(grid,1,1). Since z is
548       // not used for the number of thread blocks, it is omitted in the op.
549       grid_x = builder.createConvert(
550           loc, i32Ty,
551           fir::getBase(converter.genExprValue(
552               caller.getCallDescription().chevrons()[0], stmtCtx)));
553       grid_y = one;
554       grid_z = one;
555     } else {
556       auto dim3Addr = converter.genExprAddr(
557           caller.getCallDescription().chevrons()[0], stmtCtx);
558       grid_x = readDim3Value(builder, loc, fir::getBase(dim3Addr), "x");
559       grid_y = readDim3Value(builder, loc, fir::getBase(dim3Addr), "y");
560       grid_z = readDim3Value(builder, loc, fir::getBase(dim3Addr), "z");
561     }
562 
563     mlir::Value block_x, block_y, block_z;
564     if (caller.getCallDescription().chevrons()[1].GetType()->category() ==
565         Fortran::common::TypeCategory::Integer) {
566       // If block is an integer, it is converted to dim3(block,1,1).
567       block_x = builder.createConvert(
568           loc, i32Ty,
569           fir::getBase(converter.genExprValue(
570               caller.getCallDescription().chevrons()[1], stmtCtx)));
571       block_y = one;
572       block_z = one;
573     } else {
574       auto dim3Addr = converter.genExprAddr(
575           caller.getCallDescription().chevrons()[1], stmtCtx);
576       block_x = readDim3Value(builder, loc, fir::getBase(dim3Addr), "x");
577       block_y = readDim3Value(builder, loc, fir::getBase(dim3Addr), "y");
578       block_z = readDim3Value(builder, loc, fir::getBase(dim3Addr), "z");
579     }
580 
581     mlir::Value bytes; // bytes is optional.
582     if (caller.getCallDescription().chevrons().size() > 2)
583       bytes = builder.createConvert(
584           loc, i32Ty,
585           fir::getBase(converter.genExprValue(
586               caller.getCallDescription().chevrons()[2], stmtCtx)));
587 
588     mlir::Value stream; // stream is optional.
589     if (caller.getCallDescription().chevrons().size() > 3)
590       stream = builder.createConvert(
591           loc, i32Ty,
592           fir::getBase(converter.genExprValue(
593               caller.getCallDescription().chevrons()[3], stmtCtx)));
594 
595     builder.create<cuf::KernelLaunchOp>(
596         loc, funcType.getResults(), funcSymbolAttr, grid_x, grid_y, grid_z,
597         block_x, block_y, block_z, bytes, stream, operands);
598     callNumResults = 0;
599   } else if (caller.requireDispatchCall()) {
600     // Procedure call requiring a dynamic dispatch. Call is created with
601     // fir.dispatch.
602 
603     // Get the raw procedure name. The procedure name is not mangled in the
604     // binding table, but there can be a suffix to distinguish bindings of
605     // the same name (which happens only when PRIVATE bindings exist in
606     // ancestor types in other modules).
607     const auto &ultimateSymbol =
608         caller.getCallDescription().proc().GetSymbol()->GetUltimate();
609     std::string procName = ultimateSymbol.name().ToString();
610     if (const auto &binding{
611             ultimateSymbol.get<Fortran::semantics::ProcBindingDetails>()};
612         binding.numPrivatesNotOverridden() > 0)
613       procName += "."s + std::to_string(binding.numPrivatesNotOverridden());
614     fir::DispatchOp dispatch;
615     if (std::optional<unsigned> passArg = caller.getPassArgIndex()) {
616       // PASS, PASS(arg-name)
617       // Note that caller.getInputs is used instead of operands to get the
618       // passed object because interface mismatch issues may have inserted a
619       // cast to the operand with a different declared type, which would break
620       // later type bound call resolution in the FIR to FIR pass.
621       dispatch = builder.create<fir::DispatchOp>(
622           loc, funcType.getResults(), builder.getStringAttr(procName),
623           caller.getInputs()[*passArg], operands,
624           builder.getI32IntegerAttr(*passArg), procAttrs);
625     } else {
626       // NOPASS
627       const Fortran::evaluate::Component *component =
628           caller.getCallDescription().proc().GetComponent();
629       assert(component && "expect component for type-bound procedure call.");
630 
631       fir::ExtendedValue dataRefValue = Fortran::lower::convertDataRefToValue(
632           loc, converter, component->base(), symMap, stmtCtx);
633       mlir::Value passObject = fir::getBase(dataRefValue);
634 
635       if (fir::isa_ref_type(passObject.getType()))
636         passObject = builder.create<fir::LoadOp>(loc, passObject);
637       dispatch = builder.create<fir::DispatchOp>(
638           loc, funcType.getResults(), builder.getStringAttr(procName),
639           passObject, operands, nullptr, procAttrs);
640     }
641     callNumResults = dispatch.getNumResults();
642     if (callNumResults != 0)
643       callResult = dispatch.getResult(0);
644   } else {
645     // Standard procedure call with fir.call.
646     auto call = builder.create<fir::CallOp>(
647         loc, funcType.getResults(), funcSymbolAttr, operands, procAttrs);
648 
649     callNumResults = call.getNumResults();
650     if (callNumResults != 0)
651       callResult = call.getResult(0);
652   }
653 
654   std::optional<Fortran::evaluate::DynamicType> retTy =
655       caller.getCallDescription().proc().GetType();
656   // With HLFIR lowering, isElemental must be set to true
657   // if we are producing an elemental call. In this case,
658   // the elemental results must not be destroyed, instead,
659   // the resulting array result will be finalized/destroyed
660   // as needed by hlfir.destroy.
661   const bool mustFinalizeResult =
662       !isElemental && callSiteType.getNumResults() > 0 &&
663       !fir::isPointerType(callSiteType.getResult(0)) && retTy.has_value() &&
664       (retTy->category() == Fortran::common::TypeCategory::Derived ||
665        retTy->IsPolymorphic() || retTy->IsUnlimitedPolymorphic());
666 
667   if (caller.mustSaveResult()) {
668     assert(allocatedResult.has_value());
669     builder.create<fir::SaveResultOp>(loc, callResult,
670                                       fir::getBase(*allocatedResult),
671                                       arrayResultShape, resultLengths);
672   }
673 
674   if (evaluateInMemory) {
675     builder.setInsertionPointAfter(evaluateInMemory);
676     mlir::Value expr = evaluateInMemory.getResult();
677     fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
678     if (!isElemental)
679       stmtCtx.attachCleanup([bldr, loc, expr, mustFinalizeResult]() {
680         bldr->create<hlfir::DestroyOp>(loc, expr,
681                                        /*finalize=*/mustFinalizeResult);
682       });
683     return {LoweredResult{hlfir::EntityWithAttributes{expr}},
684             mustFinalizeResult};
685   }
686 
687   if (allocatedResult) {
688     // The result must be optionally destroyed (if it is of a derived type
689     // that may need finalization or deallocation of the components).
690     // For an allocatable result we have to free the memory allocated
691     // for the top-level entity. Note that the Destroy calls below
692     // do not deallocate the top-level entity. The two clean-ups
693     // must be pushed in reverse order, so that the final order is:
694     //   Destroy(desc)
695     //   free(desc->base_addr)
696     allocatedResult->match(
697         [&](const fir::MutableBoxValue &box) {
698           if (box.isAllocatable()) {
699             // 9.7.3.2 point 4. Deallocate allocatable results. Note that
700             // finalization was done independently by calling
701             // genDerivedTypeDestroy above and is not triggered by this inline
702             // deallocation.
703             fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
704             stmtCtx.attachCleanup([bldr, loc, box]() {
705               fir::factory::genFreememIfAllocated(*bldr, loc, box);
706             });
707           }
708         },
709         [](const auto &) {});
710 
711     // 7.5.6.3 point 5. Derived-type finalization for nonpointer function.
712     bool resultIsFinalized = false;
713     // Check if the derived-type is finalizable if it is a monomorphic
714     // derived-type.
715     // For polymorphic and unlimited polymorphic enities call the runtime
716     // in any cases.
717     if (mustFinalizeResult) {
718       if (retTy->IsPolymorphic() || retTy->IsUnlimitedPolymorphic()) {
719         auto *bldr = &converter.getFirOpBuilder();
720         stmtCtx.attachCleanup([bldr, loc, allocatedResult]() {
721           fir::runtime::genDerivedTypeDestroy(*bldr, loc,
722                                               fir::getBase(*allocatedResult));
723         });
724         resultIsFinalized = true;
725       } else {
726         const Fortran::semantics::DerivedTypeSpec &typeSpec =
727             retTy->GetDerivedTypeSpec();
728         // If the result type may require finalization
729         // or have allocatable components, we need to make sure
730         // everything is properly finalized/deallocated.
731         if (Fortran::semantics::MayRequireFinalization(typeSpec) ||
732             // We can use DerivedTypeDestroy even if finalization is not needed.
733             hlfir::mayHaveAllocatableComponent(funcType.getResults()[0])) {
734           auto *bldr = &converter.getFirOpBuilder();
735           stmtCtx.attachCleanup([bldr, loc, allocatedResult]() {
736             mlir::Value box = bldr->createBox(loc, *allocatedResult);
737             fir::runtime::genDerivedTypeDestroy(*bldr, loc, box);
738           });
739           resultIsFinalized = true;
740         }
741       }
742     }
743     return {LoweredResult{*allocatedResult}, resultIsFinalized};
744   }
745 
746   // subroutine call
747   if (!resultType)
748     return {LoweredResult{fir::ExtendedValue{mlir::Value{}}},
749             /*resultIsFinalized=*/false};
750 
751   // For now, Fortran return values are implemented with a single MLIR
752   // function return value.
753   assert(callNumResults == 1 && "Expected exactly one result in FUNCTION call");
754   (void)callNumResults;
755 
756   // Call a BIND(C) function that return a char.
757   if (caller.characterize().IsBindC() &&
758       mlir::isa<fir::CharacterType>(funcType.getResults()[0])) {
759     fir::CharacterType charTy =
760         mlir::dyn_cast<fir::CharacterType>(funcType.getResults()[0]);
761     mlir::Value len = builder.createIntegerConstant(
762         loc, builder.getCharacterLengthType(), charTy.getLen());
763     return {
764         LoweredResult{fir::ExtendedValue{fir::CharBoxValue{callResult, len}}},
765         /*resultIsFinalized=*/false};
766   }
767 
768   return {LoweredResult{fir::ExtendedValue{callResult}},
769           /*resultIsFinalized=*/false};
770 }
771 
772 static hlfir::EntityWithAttributes genStmtFunctionRef(
773     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
774     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
775     const Fortran::evaluate::ProcedureRef &procRef) {
776   const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol();
777   assert(symbol && "expected symbol in ProcedureRef of statement functions");
778   const auto &details = symbol->get<Fortran::semantics::SubprogramDetails>();
779   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
780 
781   // Statement functions have their own scope, we just need to associate
782   // the dummy symbols to argument expressions. There are no
783   // optional/alternate return arguments. Statement functions cannot be
784   // recursive (directly or indirectly) so it is safe to add dummy symbols to
785   // the local map here.
786   symMap.pushScope();
787   llvm::SmallVector<hlfir::AssociateOp> exprAssociations;
788   for (auto [arg, bind] : llvm::zip(details.dummyArgs(), procRef.arguments())) {
789     assert(arg && "alternate return in statement function");
790     assert(bind && "optional argument in statement function");
791     const auto *expr = bind->UnwrapExpr();
792     // TODO: assumed type in statement function, that surprisingly seems
793     // allowed, probably because nobody thought of restricting this usage.
794     // gfortran/ifort compiles this.
795     assert(expr && "assumed type used as statement function argument");
796     // As per Fortran 2018 C1580, statement function arguments can only be
797     // scalars.
798     // The only care is to use the dummy character explicit length if any
799     // instead of the actual argument length (that can be bigger).
800     hlfir::EntityWithAttributes loweredArg = Fortran::lower::convertExprToHLFIR(
801         loc, converter, *expr, symMap, stmtCtx);
802     fir::FortranVariableOpInterface variableIface = loweredArg.getIfVariable();
803     if (!variableIface) {
804       // So far only FortranVariableOpInterface can be mapped to symbols.
805       // Create an hlfir.associate to create a variable from a potential
806       // value argument.
807       mlir::Type argType = converter.genType(*arg);
808       auto associate = hlfir::genAssociateExpr(
809           loc, builder, loweredArg, argType, toStringRef(arg->name()));
810       exprAssociations.push_back(associate);
811       variableIface = associate;
812     }
813     const Fortran::semantics::DeclTypeSpec *type = arg->GetType();
814     if (type &&
815         type->category() == Fortran::semantics::DeclTypeSpec::Character) {
816       // Instantiate character as if it was a normal dummy argument so that the
817       // statement function dummy character length is applied and dealt with
818       // correctly.
819       symMap.addSymbol(*arg, variableIface.getBase());
820       Fortran::lower::mapSymbolAttributes(converter, *arg, symMap, stmtCtx);
821     } else {
822       // No need to create an extra hlfir.declare otherwise for
823       // numerical and logical scalar dummies.
824       symMap.addVariableDefinition(*arg, variableIface);
825     }
826   }
827 
828   // Explicitly map statement function host associated symbols to their
829   // parent scope lowered symbol box.
830   for (const Fortran::semantics::SymbolRef &sym :
831        Fortran::evaluate::CollectSymbols(*details.stmtFunction()))
832     if (const auto *details =
833             sym->detailsIf<Fortran::semantics::HostAssocDetails>())
834       converter.copySymbolBinding(details->symbol(), sym);
835 
836   hlfir::Entity result = Fortran::lower::convertExprToHLFIR(
837       loc, converter, details.stmtFunction().value(), symMap, stmtCtx);
838   symMap.popScope();
839   // The result must not be a variable.
840   result = hlfir::loadTrivialScalar(loc, builder, result);
841   if (result.isVariable())
842     result = hlfir::Entity{builder.create<hlfir::AsExprOp>(loc, result)};
843   for (auto associate : exprAssociations)
844     builder.create<hlfir::EndAssociateOp>(loc, associate);
845   return hlfir::EntityWithAttributes{result};
846 }
847 
848 namespace {
849 // Structure to hold the information about the call and the lowering context.
850 // This structure is intended to help threading the information
851 // through the various lowering calls without having to pass every
852 // required structure one by one.
853 struct CallContext {
854   CallContext(const Fortran::evaluate::ProcedureRef &procRef,
855               std::optional<mlir::Type> resultType, mlir::Location loc,
856               Fortran::lower::AbstractConverter &converter,
857               Fortran::lower::SymMap &symMap,
858               Fortran::lower::StatementContext &stmtCtx)
859       : procRef{procRef}, converter{converter}, symMap{symMap},
860         stmtCtx{stmtCtx}, resultType{resultType}, loc{loc} {}
861 
862   fir::FirOpBuilder &getBuilder() { return converter.getFirOpBuilder(); }
863 
864   std::string getProcedureName() const {
865     if (const Fortran::semantics::Symbol *sym = procRef.proc().GetSymbol())
866       return sym->GetUltimate().name().ToString();
867     return procRef.proc().GetName();
868   }
869 
870   /// Is this a call to an elemental procedure with at least one array argument?
871   bool isElementalProcWithArrayArgs() const {
872     if (procRef.IsElemental())
873       for (const std::optional<Fortran::evaluate::ActualArgument> &arg :
874            procRef.arguments())
875         if (arg && arg->Rank() != 0)
876           return true;
877     return false;
878   }
879 
880   /// Is this a statement function reference?
881   bool isStatementFunctionCall() const {
882     if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
883       if (const auto *details =
884               symbol->detailsIf<Fortran::semantics::SubprogramDetails>())
885         return details->stmtFunction().has_value();
886     return false;
887   }
888 
889   /// Is this a call to a BIND(C) procedure?
890   bool isBindcCall() const {
891     if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
892       return Fortran::semantics::IsBindCProcedure(*symbol);
893     return false;
894   }
895 
896   const Fortran::evaluate::ProcedureRef &procRef;
897   Fortran::lower::AbstractConverter &converter;
898   Fortran::lower::SymMap &symMap;
899   Fortran::lower::StatementContext &stmtCtx;
900   std::optional<mlir::Type> resultType;
901   mlir::Location loc;
902 };
903 
904 using ExvAndCleanup =
905     std::pair<fir::ExtendedValue, std::optional<hlfir::CleanupFunction>>;
906 } // namespace
907 
908 // Helper to transform a fir::ExtendedValue to an hlfir::EntityWithAttributes.
909 static hlfir::EntityWithAttributes
910 extendedValueToHlfirEntity(mlir::Location loc, fir::FirOpBuilder &builder,
911                            const fir::ExtendedValue &exv,
912                            llvm::StringRef name) {
913   mlir::Value firBase = fir::getBase(exv);
914   mlir::Type firBaseTy = firBase.getType();
915   if (fir::isa_trivial(firBaseTy))
916     return hlfir::EntityWithAttributes{firBase};
917   if (auto charTy = mlir::dyn_cast<fir::CharacterType>(firBase.getType())) {
918     // CHAR() intrinsic and BIND(C) procedures returning CHARACTER(1)
919     // are lowered to a fir.char<kind,1> that is not in memory.
920     // This tends to cause a lot of bugs because the rest of the
921     // infrastructure is mostly tested with characters that are
922     // in memory.
923     // To avoid having to deal with this special case here and there,
924     // place it in memory here. If this turns out to be suboptimal,
925     // this could be fixed, but for now llvm opt -O1 is able to get
926     // rid of the memory indirection in a = char(b), so there is
927     // little incentive to increase the compiler complexity.
928     hlfir::Entity storage{builder.createTemporary(loc, charTy)};
929     builder.create<fir::StoreOp>(loc, firBase, storage);
930     auto asExpr = builder.create<hlfir::AsExprOp>(
931         loc, storage, /*mustFree=*/builder.createBool(loc, false));
932     return hlfir::EntityWithAttributes{asExpr.getResult()};
933   }
934   return hlfir::genDeclare(loc, builder, exv, name,
935                            fir::FortranVariableFlagsAttr{});
936 }
937 namespace {
938 /// Structure to hold the clean-up related to a dummy argument preparation
939 /// that may have to be done after a call (copy-out or temporary deallocation).
940 struct CallCleanUp {
941   struct CopyIn {
942     void genCleanUp(mlir::Location loc, fir::FirOpBuilder &builder) {
943       builder.create<hlfir::CopyOutOp>(loc, tempBox, wasCopied, copyBackVar);
944     }
945     // address of the descriptor holding the temp if a temp was created.
946     mlir::Value tempBox;
947     // Boolean indicating if a copy was made or not.
948     mlir::Value wasCopied;
949     // copyBackVar may be null if copy back is not needed.
950     mlir::Value copyBackVar;
951   };
952   struct ExprAssociate {
953     void genCleanUp(mlir::Location loc, fir::FirOpBuilder &builder) {
954       builder.create<hlfir::EndAssociateOp>(loc, tempVar, mustFree);
955     }
956     mlir::Value tempVar;
957     mlir::Value mustFree;
958   };
959   void genCleanUp(mlir::Location loc, fir::FirOpBuilder &builder) {
960     Fortran::common::visit([&](auto &c) { c.genCleanUp(loc, builder); },
961                            cleanUp);
962   }
963   std::variant<CopyIn, ExprAssociate> cleanUp;
964 };
965 
966 /// Structure representing a prepared dummy argument.
967 /// It holds the value to be passed in the call and any related
968 /// clean-ups to be done after the call.
969 struct PreparedDummyArgument {
970   void pushCopyInCleanUp(mlir::Value tempBox, mlir::Value wasCopied,
971                          mlir::Value copyBackVar) {
972     cleanups.emplace_back(
973         CallCleanUp{CallCleanUp::CopyIn{tempBox, wasCopied, copyBackVar}});
974   }
975   void pushExprAssociateCleanUp(mlir::Value tempVar, mlir::Value wasCopied) {
976     cleanups.emplace_back(
977         CallCleanUp{CallCleanUp::ExprAssociate{tempVar, wasCopied}});
978   }
979   void pushExprAssociateCleanUp(hlfir::AssociateOp associate) {
980     mlir::Value hlfirBase = associate.getBase();
981     mlir::Value firBase = associate.getFirBase();
982     cleanups.emplace_back(CallCleanUp{CallCleanUp::ExprAssociate{
983         hlfir::mayHaveAllocatableComponent(hlfirBase.getType()) ? hlfirBase
984                                                                 : firBase,
985         associate.getMustFreeStrorageFlag()}});
986   }
987 
988   mlir::Value dummy;
989   // NOTE: the clean-ups are executed in reverse order.
990   llvm::SmallVector<CallCleanUp, 2> cleanups;
991 };
992 
993 /// Structure to help conditionally preparing a dummy argument based
994 /// on the actual argument presence.
995 /// It helps "wrapping" the dummy and the clean-up information in
996 /// an if (present) {...}:
997 ///
998 ///  %conditionallyPrepared = fir.if (%present) {
999 ///    fir.result %preparedDummy
1000 ///  } else {
1001 ///    fir.result %absent
1002 ///  }
1003 ///
1004 struct ConditionallyPreparedDummy {
1005   /// Create ConditionallyPreparedDummy from a preparedDummy that must
1006   /// be wrapped in a fir.if.
1007   ConditionallyPreparedDummy(PreparedDummyArgument &preparedDummy) {
1008     thenResultValues.push_back(preparedDummy.dummy);
1009     for (const CallCleanUp &c : preparedDummy.cleanups) {
1010       if (const auto *copyInCleanUp =
1011               std::get_if<CallCleanUp::CopyIn>(&c.cleanUp)) {
1012         thenResultValues.push_back(copyInCleanUp->wasCopied);
1013         if (copyInCleanUp->copyBackVar)
1014           thenResultValues.push_back(copyInCleanUp->copyBackVar);
1015       } else {
1016         const auto &exprAssociate =
1017             std::get<CallCleanUp::ExprAssociate>(c.cleanUp);
1018         thenResultValues.push_back(exprAssociate.tempVar);
1019         thenResultValues.push_back(exprAssociate.mustFree);
1020       }
1021     }
1022   }
1023 
1024   /// Get the result types of the wrapping fir.if that must be created.
1025   llvm::SmallVector<mlir::Type> getIfResulTypes() const {
1026     llvm::SmallVector<mlir::Type> types;
1027     for (mlir::Value res : thenResultValues)
1028       types.push_back(res.getType());
1029     return types;
1030   }
1031 
1032   /// Generate the "fir.result %preparedDummy" in the then branch of the
1033   /// wrapping fir.if.
1034   void genThenResult(mlir::Location loc, fir::FirOpBuilder &builder) const {
1035     builder.create<fir::ResultOp>(loc, thenResultValues);
1036   }
1037 
1038   /// Generate the "fir.result %absent" in the else branch of the
1039   /// wrapping fir.if.
1040   void genElseResult(mlir::Location loc, fir::FirOpBuilder &builder) const {
1041     llvm::SmallVector<mlir::Value> elseResultValues;
1042     mlir::Type i1Type = builder.getI1Type();
1043     for (mlir::Value res : thenResultValues) {
1044       mlir::Type type = res.getType();
1045       if (type == i1Type)
1046         elseResultValues.push_back(builder.createBool(loc, false));
1047       else
1048         elseResultValues.push_back(builder.genAbsentOp(loc, type));
1049     }
1050     builder.create<fir::ResultOp>(loc, elseResultValues);
1051   }
1052 
1053   /// Once the fir.if has been created, get the resulting %conditionallyPrepared
1054   /// dummy argument.
1055   PreparedDummyArgument
1056   getPreparedDummy(fir::IfOp ifOp,
1057                    const PreparedDummyArgument &unconditionalDummy) {
1058     PreparedDummyArgument preparedDummy;
1059     preparedDummy.dummy = ifOp.getResults()[0];
1060     for (const CallCleanUp &c : unconditionalDummy.cleanups) {
1061       if (const auto *copyInCleanUp =
1062               std::get_if<CallCleanUp::CopyIn>(&c.cleanUp)) {
1063         mlir::Value copyBackVar;
1064         if (copyInCleanUp->copyBackVar)
1065           copyBackVar = ifOp.getResults().back();
1066         // tempBox is an hlfir.copy_in argument created outside of the
1067         // fir.if region. It needs not to be threaded as a fir.if result.
1068         preparedDummy.pushCopyInCleanUp(copyInCleanUp->tempBox,
1069                                         ifOp.getResults()[1], copyBackVar);
1070       } else {
1071         preparedDummy.pushExprAssociateCleanUp(ifOp.getResults()[1],
1072                                                ifOp.getResults()[2]);
1073       }
1074     }
1075     return preparedDummy;
1076   }
1077 
1078   llvm::SmallVector<mlir::Value> thenResultValues;
1079 };
1080 } // namespace
1081 
1082 /// Fix-up the fact that it is supported to pass a character procedure
1083 /// designator to a non character procedure dummy procedure and vice-versa, even
1084 /// in case of explicit interface. Uglier cases where an object is passed as
1085 /// procedure designator or vice versa are handled only for implicit interfaces
1086 /// (refused by semantics with explicit interface), and handled with a funcOp
1087 /// cast like other implicit interface mismatches.
1088 static hlfir::Entity fixProcedureDummyMismatch(mlir::Location loc,
1089                                                fir::FirOpBuilder &builder,
1090                                                hlfir::Entity actual,
1091                                                mlir::Type dummyType) {
1092   if (mlir::isa<fir::BoxProcType>(actual.getType()) &&
1093       fir::isCharacterProcedureTuple(dummyType)) {
1094     mlir::Value length =
1095         builder.create<fir::UndefOp>(loc, builder.getCharacterLengthType());
1096     mlir::Value tuple = fir::factory::createCharacterProcedureTuple(
1097         builder, loc, dummyType, actual, length);
1098     return hlfir::Entity{tuple};
1099   }
1100   assert(fir::isCharacterProcedureTuple(actual.getType()) &&
1101          mlir::isa<fir::BoxProcType>(dummyType) &&
1102          "unsupported dummy procedure mismatch with the actual argument");
1103   mlir::Value boxProc = fir::factory::extractCharacterProcedureTuple(
1104                             builder, loc, actual, /*openBoxProc=*/false)
1105                             .first;
1106   return hlfir::Entity{boxProc};
1107 }
1108 
1109 mlir::Value static getZeroLowerBounds(mlir::Location loc,
1110                                       fir::FirOpBuilder &builder,
1111                                       hlfir::Entity entity) {
1112   assert(!entity.isAssumedRank() &&
1113          "assumed-rank must use fir.rebox_assumed_rank");
1114   if (entity.getRank() < 1)
1115     return {};
1116   mlir::Value zero =
1117       builder.createIntegerConstant(loc, builder.getIndexType(), 0);
1118   llvm::SmallVector<mlir::Value> lowerBounds(entity.getRank(), zero);
1119   return builder.genShift(loc, lowerBounds);
1120 }
1121 
1122 static bool
1123 isSimplyContiguous(const Fortran::evaluate::ActualArgument &arg,
1124                    Fortran::evaluate::FoldingContext &foldingContext) {
1125   if (const auto *expr = arg.UnwrapExpr())
1126     return Fortran::evaluate::IsSimplyContiguous(*expr, foldingContext);
1127   const Fortran::semantics::Symbol *sym = arg.GetAssumedTypeDummy();
1128   assert(sym &&
1129          "expect ActualArguments to be expression or assumed-type symbols");
1130   return sym->Rank() == 0 ||
1131          Fortran::evaluate::IsSimplyContiguous(*sym, foldingContext);
1132 }
1133 
1134 /// When dummy is not ALLOCATABLE, POINTER and is not passed in register,
1135 /// prepare the actual argument according to the interface. Do as needed:
1136 /// - address element if this is an array argument in an elemental call.
1137 /// - set dynamic type to the dummy type if the dummy is not polymorphic.
1138 /// - copy-in into contiguous variable if the dummy must be contiguous
1139 /// - copy into a temporary if the dummy has the VALUE attribute.
1140 /// - package the prepared dummy as required (fir.box, fir.class,
1141 ///   fir.box_char...).
1142 /// This function should only be called with an actual that is present.
1143 /// The optional aspects must be handled by this function user.
1144 static PreparedDummyArgument preparePresentUserCallActualArgument(
1145     mlir::Location loc, fir::FirOpBuilder &builder,
1146     const Fortran::lower::PreparedActualArgument &preparedActual,
1147     mlir::Type dummyType,
1148     const Fortran::lower::CallerInterface::PassedEntity &arg,
1149     CallContext &callContext) {
1150 
1151   Fortran::evaluate::FoldingContext &foldingContext =
1152       callContext.converter.getFoldingContext();
1153 
1154   // Step 1: get the actual argument, which includes addressing the
1155   // element if this is an array in an elemental call.
1156   hlfir::Entity actual = preparedActual.getActual(loc, builder);
1157 
1158   // Handle procedure arguments (procedure pointers should go through
1159   // prepareProcedurePointerActualArgument).
1160   if (hlfir::isFortranProcedureValue(dummyType)) {
1161     // Procedure pointer or function returns procedure pointer actual to
1162     // procedure dummy.
1163     if (actual.isProcedurePointer()) {
1164       actual = hlfir::derefPointersAndAllocatables(loc, builder, actual);
1165       return PreparedDummyArgument{actual, /*cleanups=*/{}};
1166     }
1167     // Procedure actual to procedure dummy.
1168     assert(actual.isProcedure());
1169     // Do nothing if this is a procedure argument. It is already a
1170     // fir.boxproc/fir.tuple<fir.boxproc, len> as it should.
1171     if (!mlir::isa<fir::BoxProcType>(actual.getType()) &&
1172         actual.getType() != dummyType)
1173       // The actual argument may be a procedure that returns character (a
1174       // fir.tuple<fir.boxproc, len>) while the dummy is not. Extract the tuple
1175       // in that case.
1176       actual = fixProcedureDummyMismatch(loc, builder, actual, dummyType);
1177     return PreparedDummyArgument{actual, /*cleanups=*/{}};
1178   }
1179 
1180   const bool ignoreTKRtype = arg.testTKR(Fortran::common::IgnoreTKR::Type);
1181   const bool passingPolymorphicToNonPolymorphic =
1182       actual.isPolymorphic() && !fir::isPolymorphicType(dummyType) &&
1183       !ignoreTKRtype;
1184 
1185   // When passing a CLASS(T) to TYPE(T), only the "T" part must be
1186   // passed. Unless the entity is a scalar passed by raw address, a
1187   // new descriptor must be made using the dummy argument type as
1188   // dynamic type. This must be done before any copy/copy-in because the
1189   // dynamic type matters to determine the contiguity.
1190   const bool mustSetDynamicTypeToDummyType =
1191       passingPolymorphicToNonPolymorphic &&
1192       (actual.isArray() || mlir::isa<fir::BaseBoxType>(dummyType));
1193 
1194   // The simple contiguity of the actual is "lost" when passing a polymorphic
1195   // to a non polymorphic entity because the dummy dynamic type matters for
1196   // the contiguity.
1197   const bool mustDoCopyInOut =
1198       actual.isArray() && arg.mustBeMadeContiguous() &&
1199       (passingPolymorphicToNonPolymorphic ||
1200        !isSimplyContiguous(*arg.entity, foldingContext));
1201 
1202   const bool actualIsAssumedRank = actual.isAssumedRank();
1203   // Create dummy type with actual argument rank when the dummy is an assumed
1204   // rank. That way, all the operation to create dummy descriptors are ranked if
1205   // the actual argument is ranked, which allows simple code generation.
1206   // Also do the same when the dummy is a sequence associated descriptor
1207   // because the actual shape/rank may mismatch with the dummy, and the dummy
1208   // may be an assumed-size array, so any descriptor manipulation should use the
1209   // actual argument shape information. A descriptor with the dummy shape
1210   // information will be created later when all actual arguments are ready.
1211   mlir::Type dummyTypeWithActualRank = dummyType;
1212   if (auto baseBoxDummy = mlir::dyn_cast<fir::BaseBoxType>(dummyType)) {
1213     if (baseBoxDummy.isAssumedRank() ||
1214         arg.testTKR(Fortran::common::IgnoreTKR::Rank) ||
1215         arg.isSequenceAssociatedDescriptor()) {
1216       mlir::Type actualTy =
1217           hlfir::getFortranElementOrSequenceType(actual.getType());
1218       dummyTypeWithActualRank = baseBoxDummy.getBoxTypeWithNewShape(actualTy);
1219     }
1220   }
1221   // Preserve the actual type in the argument preparation in case IgnoreTKR(t)
1222   // is set (descriptors must be created with the actual type in this case, and
1223   // copy-in/copy-out should be driven by the contiguity with regard to the
1224   // actual type).
1225   if (ignoreTKRtype) {
1226     if (auto boxCharType =
1227             mlir::dyn_cast<fir::BoxCharType>(dummyTypeWithActualRank)) {
1228       auto maybeActualCharType =
1229           mlir::dyn_cast<fir::CharacterType>(actual.getFortranElementType());
1230       if (!maybeActualCharType ||
1231           maybeActualCharType.getFKind() != boxCharType.getKind()) {
1232         // When passing to a fir.boxchar with ignore(tk), prepare the argument
1233         // as if only the raw address must be passed.
1234         dummyTypeWithActualRank =
1235             fir::ReferenceType::get(actual.getElementOrSequenceType());
1236       }
1237       // Otherwise, the actual is already a character with the same kind as the
1238       // dummy and can be passed normally.
1239     } else {
1240       dummyTypeWithActualRank = fir::changeElementType(
1241           dummyTypeWithActualRank, actual.getFortranElementType(),
1242           actual.isPolymorphic());
1243     }
1244   }
1245 
1246   PreparedDummyArgument preparedDummy;
1247 
1248   // Helpers to generate hlfir.copy_in operation and register the related
1249   // hlfir.copy_out creation.
1250   auto genCopyIn = [&](hlfir::Entity var, bool doCopyOut) -> hlfir::Entity {
1251     auto baseBoxTy = mlir::dyn_cast<fir::BaseBoxType>(var.getType());
1252     assert(baseBoxTy && "expect non simply contiguous variables to be boxes");
1253     // Create allocatable descriptor for the potential temporary.
1254     mlir::Type tempBoxType = baseBoxTy.getBoxTypeWithNewAttr(
1255         fir::BaseBoxType::Attribute::Allocatable);
1256     mlir::Value tempBox = builder.createTemporary(loc, tempBoxType);
1257     auto copyIn = builder.create<hlfir::CopyInOp>(
1258         loc, var, tempBox, /*var_is_present=*/mlir::Value{});
1259     // Register the copy-out after the call.
1260     preparedDummy.pushCopyInCleanUp(copyIn.getTempBox(), copyIn.getWasCopied(),
1261                                     doCopyOut ? copyIn.getVar()
1262                                               : mlir::Value{});
1263     return hlfir::Entity{copyIn.getCopiedIn()};
1264   };
1265 
1266   auto genSetDynamicTypeToDummyType = [&](hlfir::Entity var) -> hlfir::Entity {
1267     fir::BaseBoxType boxType = fir::BoxType::get(
1268         hlfir::getFortranElementOrSequenceType(dummyTypeWithActualRank));
1269     if (actualIsAssumedRank)
1270       return hlfir::Entity{builder.create<fir::ReboxAssumedRankOp>(
1271           loc, boxType, var, fir::LowerBoundModifierAttribute::SetToOnes)};
1272     // Use actual shape when creating descriptor with dummy type, the dummy
1273     // shape may be unknown in case of sequence association.
1274     mlir::Type actualTy =
1275         hlfir::getFortranElementOrSequenceType(actual.getType());
1276     boxType = boxType.getBoxTypeWithNewShape(actualTy);
1277     return hlfir::Entity{builder.create<fir::ReboxOp>(loc, boxType, var,
1278                                                       /*shape=*/mlir::Value{},
1279                                                       /*slice=*/mlir::Value{})};
1280   };
1281 
1282   // Step 2: prepare the storage for the dummy arguments, ensuring that it
1283   // matches the dummy requirements (e.g., must be contiguous or must be
1284   // a temporary).
1285   hlfir::Entity entity =
1286       hlfir::derefPointersAndAllocatables(loc, builder, actual);
1287   if (entity.isVariable()) {
1288     // Set dynamic type if needed before any copy-in or copy so that the dummy
1289     // is contiguous according to the dummy type.
1290     if (mustSetDynamicTypeToDummyType)
1291       entity = genSetDynamicTypeToDummyType(entity);
1292     if (arg.hasValueAttribute() ||
1293         // Constant expressions might be lowered as variables with
1294         // 'parameter' attribute. Even though the constant expressions
1295         // are not definable and explicit assignments to them are not
1296         // possible, we have to create a temporary copies when we pass
1297         // them down the call stack.
1298         entity.isParameter()) {
1299       // Make a copy in a temporary.
1300       auto copy = builder.create<hlfir::AsExprOp>(loc, entity);
1301       mlir::Type storageType = entity.getType();
1302       mlir::NamedAttribute byRefAttr = fir::getAdaptToByRefAttr(builder);
1303       hlfir::AssociateOp associate = hlfir::genAssociateExpr(
1304           loc, builder, hlfir::Entity{copy}, storageType, "", byRefAttr);
1305       entity = hlfir::Entity{associate.getBase()};
1306       // Register the temporary destruction after the call.
1307       preparedDummy.pushExprAssociateCleanUp(associate);
1308     } else if (mustDoCopyInOut) {
1309       // Copy-in non contiguous variables.
1310       // TODO: for non-finalizable monomorphic derived type actual
1311       // arguments associated with INTENT(OUT) dummy arguments
1312       // we may avoid doing the copy and only allocate the temporary.
1313       // The codegen would do a "mold" allocation instead of "sourced"
1314       // allocation for the temp in this case. We can communicate
1315       // this to the codegen via some CopyInOp flag.
1316       // This is a performance concern.
1317       entity = genCopyIn(entity, arg.mayBeModifiedByCall());
1318     }
1319   } else {
1320     const Fortran::lower::SomeExpr *expr = arg.entity->UnwrapExpr();
1321     assert(expr && "expression actual argument cannot be an assumed type");
1322     // The actual is an expression value, place it into a temporary
1323     // and register the temporary destruction after the call.
1324     mlir::Type storageType = callContext.converter.genType(*expr);
1325     mlir::NamedAttribute byRefAttr = fir::getAdaptToByRefAttr(builder);
1326     hlfir::AssociateOp associate = hlfir::genAssociateExpr(
1327         loc, builder, entity, storageType, "", byRefAttr);
1328     entity = hlfir::Entity{associate.getBase()};
1329     preparedDummy.pushExprAssociateCleanUp(associate);
1330     // Rebox the actual argument to the dummy argument's type, and make sure
1331     // that we pass a contiguous entity (i.e. make copy-in, if needed).
1332     //
1333     // TODO: this can probably be optimized by associating the expression with
1334     // properly typed temporary, but this needs either a new operation or
1335     // making the hlfir.associate more complex.
1336     if (mustSetDynamicTypeToDummyType) {
1337       entity = genSetDynamicTypeToDummyType(entity);
1338       entity = genCopyIn(entity, /*doCopyOut=*/false);
1339     }
1340   }
1341 
1342   // Step 3: now that the dummy argument storage has been prepared, package
1343   // it according to the interface.
1344   mlir::Value addr;
1345   if (mlir::isa<fir::BoxCharType>(dummyTypeWithActualRank)) {
1346     addr = hlfir::genVariableBoxChar(loc, builder, entity);
1347   } else if (mlir::isa<fir::BaseBoxType>(dummyTypeWithActualRank)) {
1348     entity = hlfir::genVariableBox(loc, builder, entity);
1349     // Ensures the box has the right attributes and that it holds an
1350     // addendum if needed.
1351     fir::BaseBoxType actualBoxType =
1352         mlir::cast<fir::BaseBoxType>(entity.getType());
1353     mlir::Type boxEleType = actualBoxType.getEleTy();
1354     // For now, assume it is not OK to pass the allocatable/pointer
1355     // descriptor to a non pointer/allocatable dummy. That is a strict
1356     // interpretation of 18.3.6 point 4 that stipulates the descriptor
1357     // has the dummy attributes in BIND(C) contexts.
1358     const bool actualBoxHasAllocatableOrPointerFlag =
1359         fir::isa_ref_type(boxEleType);
1360     // Fortran 2018 18.5.3, pp3: BIND(C) non pointer allocatable descriptors
1361     // must have zero lower bounds.
1362     bool needsZeroLowerBounds = callContext.isBindcCall() && entity.isArray();
1363     // On the callee side, the current code generated for unlimited
1364     // polymorphic might unconditionally read the addendum. Intrinsic type
1365     // descriptors may not have an addendum, the rebox below will create a
1366     // descriptor with an addendum in such case.
1367     const bool actualBoxHasAddendum = fir::boxHasAddendum(actualBoxType);
1368     const bool needToAddAddendum =
1369         fir::isUnlimitedPolymorphicType(dummyTypeWithActualRank) &&
1370         !actualBoxHasAddendum;
1371     if (needToAddAddendum || actualBoxHasAllocatableOrPointerFlag ||
1372         needsZeroLowerBounds) {
1373       if (actualIsAssumedRank) {
1374         auto lbModifier = needsZeroLowerBounds
1375                               ? fir::LowerBoundModifierAttribute::SetToZeroes
1376                               : fir::LowerBoundModifierAttribute::SetToOnes;
1377         entity = hlfir::Entity{builder.create<fir::ReboxAssumedRankOp>(
1378             loc, dummyTypeWithActualRank, entity, lbModifier)};
1379       } else {
1380         mlir::Value shift{};
1381         if (needsZeroLowerBounds)
1382           shift = getZeroLowerBounds(loc, builder, entity);
1383         entity = hlfir::Entity{builder.create<fir::ReboxOp>(
1384             loc, dummyTypeWithActualRank, entity, /*shape=*/shift,
1385             /*slice=*/mlir::Value{})};
1386       }
1387     }
1388     addr = entity;
1389   } else {
1390     addr = hlfir::genVariableRawAddress(loc, builder, entity);
1391   }
1392 
1393   // For ranked actual passed to assumed-rank dummy, the cast to assumed-rank
1394   // box is inserted when building the fir.call op. Inserting it here would
1395   // cause the fir.if results to be assumed-rank in case of OPTIONAL dummy,
1396   // causing extra runtime costs due to the unknown runtime size of assumed-rank
1397   // descriptors.
1398   preparedDummy.dummy =
1399       builder.createConvert(loc, dummyTypeWithActualRank, addr);
1400   return preparedDummy;
1401 }
1402 
1403 /// When dummy is not ALLOCATABLE, POINTER and is not passed in register,
1404 /// prepare the actual argument according to the interface, taking care
1405 /// of any optional aspect.
1406 static PreparedDummyArgument prepareUserCallActualArgument(
1407     mlir::Location loc, fir::FirOpBuilder &builder,
1408     const Fortran::lower::PreparedActualArgument &preparedActual,
1409     mlir::Type dummyType,
1410     const Fortran::lower::CallerInterface::PassedEntity &arg,
1411     CallContext &callContext) {
1412   if (!preparedActual.handleDynamicOptional())
1413     return preparePresentUserCallActualArgument(loc, builder, preparedActual,
1414                                                 dummyType, arg, callContext);
1415 
1416   // Conditional dummy argument preparation. The actual may be absent
1417   // at runtime, causing any addressing, copy, and packaging to have
1418   // undefined behavior.
1419   // To simplify the handling of this case, the "normal" dummy preparation
1420   // helper is used, except its generated code is wrapped inside a
1421   // fir.if(present).
1422   mlir::Value isPresent = preparedActual.getIsPresent();
1423   mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint();
1424 
1425   // Code generated in a preparation block that will become the
1426   // "then" block in "if (present) then {} else {}". The reason
1427   // for this unusual if/then/else generation is that the number
1428   // and types of the if results will depend on how the argument
1429   // is prepared, and forecasting that here would be brittle.
1430   auto badIfOp = builder.create<fir::IfOp>(loc, dummyType, isPresent,
1431                                            /*withElseRegion=*/false);
1432   mlir::Block *preparationBlock = &badIfOp.getThenRegion().front();
1433   builder.setInsertionPointToStart(preparationBlock);
1434   PreparedDummyArgument unconditionalDummy =
1435       preparePresentUserCallActualArgument(loc, builder, preparedActual,
1436                                            dummyType, arg, callContext);
1437   builder.restoreInsertionPoint(insertPt);
1438 
1439   // TODO: when forwarding an optional to an optional of the same kind
1440   // (i.e, unconditionalDummy.dummy was not created in preparationBlock),
1441   // the if/then/else generation could be skipped to improve the generated
1442   // code.
1443 
1444   // Now that the result types of the ifOp can be deduced, generate
1445   // the "real" ifOp (operation result types cannot be changed, so
1446   // badIfOp cannot be modified and used here).
1447   llvm::SmallVector<mlir::Type> ifOpResultTypes;
1448   ConditionallyPreparedDummy conditionalDummy(unconditionalDummy);
1449   auto ifOp = builder.create<fir::IfOp>(loc, conditionalDummy.getIfResulTypes(),
1450                                         isPresent,
1451                                         /*withElseRegion=*/true);
1452   // Move "preparationBlock" into the "then" of the new
1453   // fir.if operation and create fir.result propagating
1454   // unconditionalDummy.
1455   preparationBlock->moveBefore(&ifOp.getThenRegion().back());
1456   ifOp.getThenRegion().back().erase();
1457   builder.setInsertionPointToEnd(&ifOp.getThenRegion().front());
1458   conditionalDummy.genThenResult(loc, builder);
1459 
1460   // Generate "else" branch with returning absent values.
1461   builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
1462   conditionalDummy.genElseResult(loc, builder);
1463 
1464   // Build dummy from IfOpResults.
1465   builder.setInsertionPointAfter(ifOp);
1466   PreparedDummyArgument result =
1467       conditionalDummy.getPreparedDummy(ifOp, unconditionalDummy);
1468   badIfOp->erase();
1469   return result;
1470 }
1471 
1472 /// Prepare actual argument for a procedure pointer dummy.
1473 static PreparedDummyArgument prepareProcedurePointerActualArgument(
1474     mlir::Location loc, fir::FirOpBuilder &builder,
1475     const Fortran::lower::PreparedActualArgument &preparedActual,
1476     mlir::Type dummyType,
1477     const Fortran::lower::CallerInterface::PassedEntity &arg,
1478     CallContext &callContext) {
1479 
1480   // NULL() actual to procedure pointer dummy
1481   if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(
1482           *arg.entity) &&
1483       fir::isBoxProcAddressType(dummyType)) {
1484     auto boxTy{Fortran::lower::getUntypedBoxProcType(builder.getContext())};
1485     auto tempBoxProc{builder.createTemporary(loc, boxTy)};
1486     hlfir::Entity nullBoxProc(
1487         fir::factory::createNullBoxProc(builder, loc, boxTy));
1488     builder.create<fir::StoreOp>(loc, nullBoxProc, tempBoxProc);
1489     return PreparedDummyArgument{tempBoxProc, /*cleanups=*/{}};
1490   }
1491   hlfir::Entity actual = preparedActual.getActual(loc, builder);
1492   if (actual.isProcedurePointer())
1493     return PreparedDummyArgument{actual, /*cleanups=*/{}};
1494   assert(actual.isProcedure());
1495   // Procedure actual to procedure pointer dummy.
1496   auto tempBoxProc{builder.createTemporary(loc, actual.getType())};
1497   builder.create<fir::StoreOp>(loc, actual, tempBoxProc);
1498   return PreparedDummyArgument{tempBoxProc, /*cleanups=*/{}};
1499 }
1500 
1501 /// Prepare arguments of calls to user procedures with actual arguments that
1502 /// have been pre-lowered but not yet prepared according to the interface.
1503 void prepareUserCallArguments(
1504     Fortran::lower::PreparedActualArguments &loweredActuals,
1505     Fortran::lower::CallerInterface &caller, mlir::FunctionType callSiteType,
1506     CallContext &callContext, llvm::SmallVector<CallCleanUp> &callCleanUps) {
1507   using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
1508   mlir::Location loc = callContext.loc;
1509   bool mustRemapActualToDummyDescriptors = false;
1510   fir::FirOpBuilder &builder = callContext.getBuilder();
1511   for (auto [preparedActual, arg] :
1512        llvm::zip(loweredActuals, caller.getPassedArguments())) {
1513     mlir::Type argTy = callSiteType.getInput(arg.firArgument);
1514     if (!preparedActual) {
1515       // Optional dummy argument for which there is no actual argument.
1516       caller.placeInput(arg, builder.genAbsentOp(loc, argTy));
1517       continue;
1518     }
1519 
1520     switch (arg.passBy) {
1521     case PassBy::Value: {
1522       // True pass-by-value semantics.
1523       assert(!preparedActual->handleDynamicOptional() && "cannot be optional");
1524       hlfir::Entity actual = preparedActual->getActual(loc, builder);
1525       hlfir::Entity value = hlfir::loadTrivialScalar(loc, builder, actual);
1526 
1527       mlir::Type eleTy = value.getFortranElementType();
1528       if (fir::isa_builtin_cptr_type(eleTy)) {
1529         // Pass-by-value argument of type(C_PTR/C_FUNPTR).
1530         // Load the __address component and pass it by value.
1531         if (value.isValue()) {
1532           auto associate = hlfir::genAssociateExpr(loc, builder, value, eleTy,
1533                                                    "adapt.cptrbyval");
1534           value = hlfir::Entity{genRecordCPtrValueArg(
1535               builder, loc, associate.getFirBase(), eleTy)};
1536           builder.create<hlfir::EndAssociateOp>(loc, associate);
1537         } else {
1538           value =
1539               hlfir::Entity{genRecordCPtrValueArg(builder, loc, value, eleTy)};
1540         }
1541       } else if (fir::isa_derived(value.getFortranElementType()) ||
1542                  value.isCharacter()) {
1543         // BIND(C), VALUE derived type or character. The value must really
1544         // be loaded here.
1545         auto [exv, cleanup] = hlfir::convertToValue(loc, builder, value);
1546         mlir::Value loadedValue = fir::getBase(exv);
1547         // Character actual arguments may have unknown length or a length longer
1548         // than one. Cast the memory ref to the dummy type so that the load is
1549         // valid and only loads what is needed.
1550         if (mlir::Type baseTy = fir::dyn_cast_ptrEleTy(loadedValue.getType()))
1551           if (fir::isa_char(baseTy))
1552             loadedValue = builder.createConvert(
1553                 loc, fir::ReferenceType::get(argTy), loadedValue);
1554         if (fir::isa_ref_type(loadedValue.getType()))
1555           loadedValue = builder.create<fir::LoadOp>(loc, loadedValue);
1556         caller.placeInput(arg, loadedValue);
1557         if (cleanup)
1558           (*cleanup)();
1559         break;
1560       }
1561       caller.placeInput(arg, builder.createConvert(loc, argTy, value));
1562     } break;
1563     case PassBy::BaseAddressValueAttribute:
1564     case PassBy::CharBoxValueAttribute:
1565     case PassBy::Box:
1566     case PassBy::BaseAddress:
1567     case PassBy::BoxChar: {
1568       PreparedDummyArgument preparedDummy = prepareUserCallActualArgument(
1569           loc, builder, *preparedActual, argTy, arg, callContext);
1570       callCleanUps.append(preparedDummy.cleanups.rbegin(),
1571                           preparedDummy.cleanups.rend());
1572       caller.placeInput(arg, preparedDummy.dummy);
1573       if (arg.passBy == PassBy::Box)
1574         mustRemapActualToDummyDescriptors |=
1575             arg.isSequenceAssociatedDescriptor();
1576     } break;
1577     case PassBy::BoxProcRef: {
1578       PreparedDummyArgument preparedDummy =
1579           prepareProcedurePointerActualArgument(loc, builder, *preparedActual,
1580                                                 argTy, arg, callContext);
1581       callCleanUps.append(preparedDummy.cleanups.rbegin(),
1582                           preparedDummy.cleanups.rend());
1583       caller.placeInput(arg, preparedDummy.dummy);
1584     } break;
1585     case PassBy::AddressAndLength:
1586       // PassBy::AddressAndLength is only used for character results. Results
1587       // are not handled here.
1588       fir::emitFatalError(
1589           loc, "unexpected PassBy::AddressAndLength for actual arguments");
1590       break;
1591     case PassBy::CharProcTuple: {
1592       hlfir::Entity actual = preparedActual->getActual(loc, builder);
1593       if (actual.isProcedurePointer())
1594         actual = hlfir::derefPointersAndAllocatables(loc, builder, actual);
1595       if (!fir::isCharacterProcedureTuple(actual.getType()))
1596         actual = fixProcedureDummyMismatch(loc, builder, actual, argTy);
1597       caller.placeInput(arg, actual);
1598     } break;
1599     case PassBy::MutableBox: {
1600       const Fortran::lower::SomeExpr *expr = arg.entity->UnwrapExpr();
1601       // C709 and C710.
1602       assert(expr && "cannot pass TYPE(*) to POINTER or ALLOCATABLE");
1603       hlfir::Entity actual = preparedActual->getActual(loc, builder);
1604       if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(
1605               *expr)) {
1606         // If expr is NULL(), the mutableBox created must be a deallocated
1607         // pointer with the dummy argument characteristics (see table 16.5
1608         // in Fortran 2018 standard).
1609         // No length parameters are set for the created box because any non
1610         // deferred type parameters of the dummy will be evaluated on the
1611         // callee side, and it is illegal to use NULL without a MOLD if any
1612         // dummy length parameters are assumed.
1613         mlir::Type boxTy = fir::dyn_cast_ptrEleTy(argTy);
1614         assert(boxTy && mlir::isa<fir::BaseBoxType>(boxTy) &&
1615                "must be a fir.box type");
1616         mlir::Value boxStorage =
1617             fir::factory::genNullBoxStorage(builder, loc, boxTy);
1618         caller.placeInput(arg, boxStorage);
1619         continue;
1620       }
1621       if (fir::isPointerType(argTy) &&
1622           !Fortran::evaluate::IsObjectPointer(*expr)) {
1623         // Passing a non POINTER actual argument to a POINTER dummy argument.
1624         // Create a pointer of the dummy argument type and assign the actual
1625         // argument to it.
1626         auto dataTy = llvm::cast<fir::BaseBoxType>(fir::unwrapRefType(argTy));
1627         fir::ExtendedValue actualExv = Fortran::lower::convertToAddress(
1628             loc, callContext.converter, actual, callContext.stmtCtx,
1629             hlfir::getFortranElementType(dataTy));
1630         // If the dummy is an assumed-rank pointer, allocate a pointer
1631         // descriptor with the actual argument rank (if it is not assumed-rank
1632         // itself).
1633         if (dataTy.isAssumedRank()) {
1634           dataTy =
1635               dataTy.getBoxTypeWithNewShape(fir::getBase(actualExv).getType());
1636         }
1637         mlir::Value irBox = builder.createTemporary(loc, dataTy);
1638         fir::MutableBoxValue ptrBox(irBox,
1639                                     /*nonDeferredParams=*/mlir::ValueRange{},
1640                                     /*mutableProperties=*/{});
1641         fir::factory::associateMutableBox(builder, loc, ptrBox, actualExv,
1642                                           /*lbounds=*/std::nullopt);
1643         caller.placeInput(arg, irBox);
1644         continue;
1645       }
1646       // Passing a POINTER to a POINTER, or an ALLOCATABLE to an ALLOCATABLE.
1647       assert(actual.isMutableBox() && "actual must be a mutable box");
1648       if (fir::isAllocatableType(argTy) && arg.isIntentOut() &&
1649           callContext.isBindcCall()) {
1650         // INTENT(OUT) allocatables are deallocated on the callee side,
1651         // but BIND(C) procedures may be implemented in C, so deallocation is
1652         // also done on the caller side (if the procedure is implemented in
1653         // Fortran, the deallocation attempt in the callee will be a no-op).
1654         auto [exv, cleanup] =
1655             hlfir::translateToExtendedValue(loc, builder, actual);
1656         const auto *mutableBox = exv.getBoxOf<fir::MutableBoxValue>();
1657         assert(mutableBox && !cleanup && "expect allocatable");
1658         Fortran::lower::genDeallocateIfAllocated(callContext.converter,
1659                                                  *mutableBox, loc);
1660       }
1661       caller.placeInput(arg, actual);
1662     } break;
1663     }
1664   }
1665 
1666   // Handle cases where caller must allocate the result or a fir.box for it.
1667   if (mustRemapActualToDummyDescriptors)
1668     remapActualToDummyDescriptors(loc, callContext.converter,
1669                                   callContext.symMap, loweredActuals, caller,
1670                                   callContext.isBindcCall());
1671 }
1672 
1673 /// Lower calls to user procedures with actual arguments that have been
1674 /// pre-lowered but not yet prepared according to the interface.
1675 /// This can be called for elemental procedures, but only with scalar
1676 /// arguments: if there are array arguments, it must be provided with
1677 /// the array argument elements value and will return the corresponding
1678 /// scalar result value.
1679 static std::optional<hlfir::EntityWithAttributes>
1680 genUserCall(Fortran::lower::PreparedActualArguments &loweredActuals,
1681             Fortran::lower::CallerInterface &caller,
1682             mlir::FunctionType callSiteType, CallContext &callContext) {
1683   mlir::Location loc = callContext.loc;
1684   llvm::SmallVector<CallCleanUp> callCleanUps;
1685   fir::FirOpBuilder &builder = callContext.getBuilder();
1686 
1687   prepareUserCallArguments(loweredActuals, caller, callSiteType, callContext,
1688                            callCleanUps);
1689 
1690   // Prepare lowered arguments according to the interface
1691   // and map the lowered values to the dummy
1692   // arguments.
1693   auto [loweredResult, resultIsFinalized] = Fortran::lower::genCallOpAndResult(
1694       loc, callContext.converter, callContext.symMap, callContext.stmtCtx,
1695       caller, callSiteType, callContext.resultType,
1696       callContext.isElementalProcWithArrayArgs());
1697 
1698   /// Clean-up associations and copy-in.
1699   for (auto cleanUp : callCleanUps)
1700     cleanUp.genCleanUp(loc, builder);
1701 
1702   if (auto *entity = std::get_if<hlfir::EntityWithAttributes>(&loweredResult))
1703     return *entity;
1704 
1705   auto &result = std::get<fir::ExtendedValue>(loweredResult);
1706 
1707   // For procedure pointer function result, just return the call.
1708   if (callContext.resultType &&
1709       mlir::isa<fir::BoxProcType>(*callContext.resultType))
1710     return hlfir::EntityWithAttributes(fir::getBase(result));
1711 
1712   if (!fir::getBase(result))
1713     return std::nullopt; // subroutine call.
1714 
1715   if (fir::isPointerType(fir::getBase(result).getType()))
1716     return extendedValueToHlfirEntity(loc, builder, result, tempResultName);
1717 
1718   if (!resultIsFinalized) {
1719     hlfir::Entity resultEntity =
1720         extendedValueToHlfirEntity(loc, builder, result, tempResultName);
1721     resultEntity = loadTrivialScalar(loc, builder, resultEntity);
1722     if (resultEntity.isVariable()) {
1723       // If the result has no finalization, it can be moved into an expression.
1724       // In such case, the expression should not be freed after its use since
1725       // the result is stack allocated or deallocation (for allocatable results)
1726       // was already inserted in genCallOpAndResult.
1727       auto asExpr = builder.create<hlfir::AsExprOp>(
1728           loc, resultEntity, /*mustFree=*/builder.createBool(loc, false));
1729       return hlfir::EntityWithAttributes{asExpr.getResult()};
1730     }
1731     return hlfir::EntityWithAttributes{resultEntity};
1732   }
1733   // If the result has finalization, it cannot be moved because use of its
1734   // value have been created in the statement context and may be emitted
1735   // after the hlfir.expr destroy, so the result is kept as a variable in
1736   // HLFIR. This may lead to copies when passing the result to an argument
1737   // with VALUE, and this do not convey the fact that the result will not
1738   // change, but is correct, and using hlfir.expr without the move would
1739   // trigger a copy that may be avoided.
1740 
1741   // Load allocatable results before emitting the hlfir.declare and drop its
1742   // lower bounds: this is not a variable From the Fortran point of view, so
1743   // the lower bounds are ones when inquired on the caller side.
1744   const auto *allocatable = result.getBoxOf<fir::MutableBoxValue>();
1745   fir::ExtendedValue loadedResult =
1746       allocatable
1747           ? fir::factory::genMutableBoxRead(builder, loc, *allocatable,
1748                                             /*mayBePolymorphic=*/true,
1749                                             /*preserveLowerBounds=*/false)
1750           : result;
1751   return extendedValueToHlfirEntity(loc, builder, loadedResult, tempResultName);
1752 }
1753 
1754 /// Create an optional dummy argument value from an entity that may be
1755 /// absent. \p actualGetter callback returns hlfir::Entity denoting
1756 /// the lowered actual argument. \p actualGetter can only return numerical
1757 /// or logical scalar entity.
1758 /// If the entity is considered absent according to 15.5.2.12 point 1., the
1759 /// returned value is zero (or false), otherwise it is the value of the entity.
1760 /// \p eleType specifies the entity's Fortran element type.
1761 template <typename T>
1762 static ExvAndCleanup genOptionalValue(fir::FirOpBuilder &builder,
1763                                       mlir::Location loc, mlir::Type eleType,
1764                                       T actualGetter, mlir::Value isPresent) {
1765   return {builder
1766               .genIfOp(loc, {eleType}, isPresent,
1767                        /*withElseRegion=*/true)
1768               .genThen([&]() {
1769                 hlfir::Entity entity = actualGetter(loc, builder);
1770                 assert(eleType == entity.getFortranElementType() &&
1771                        "result type mismatch in genOptionalValue");
1772                 assert(entity.isScalar() && fir::isa_trivial(eleType) &&
1773                        "must be a numerical or logical scalar");
1774                 mlir::Value val =
1775                     hlfir::loadTrivialScalar(loc, builder, entity);
1776                 builder.create<fir::ResultOp>(loc, val);
1777               })
1778               .genElse([&]() {
1779                 mlir::Value zero =
1780                     fir::factory::createZeroValue(builder, loc, eleType);
1781                 builder.create<fir::ResultOp>(loc, zero);
1782               })
1783               .getResults()[0],
1784           std::nullopt};
1785 }
1786 
1787 /// Create an optional dummy argument address from \p entity that may be
1788 /// absent. If \p entity is considered absent according to 15.5.2.12 point 1.,
1789 /// the returned value is a null pointer, otherwise it is the address of \p
1790 /// entity.
1791 static ExvAndCleanup genOptionalAddr(fir::FirOpBuilder &builder,
1792                                      mlir::Location loc, hlfir::Entity entity,
1793                                      mlir::Value isPresent) {
1794   auto [exv, cleanup] = hlfir::translateToExtendedValue(loc, builder, entity);
1795   // If it is an exv pointer/allocatable, then it cannot be absent
1796   // because it is passed to a non-pointer/non-allocatable.
1797   if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>())
1798     return {fir::factory::genMutableBoxRead(builder, loc, *box), cleanup};
1799   // If this is not a POINTER or ALLOCATABLE, then it is already an OPTIONAL
1800   // address and can be passed directly.
1801   return {exv, cleanup};
1802 }
1803 
1804 /// Create an optional dummy argument address from \p entity that may be
1805 /// absent. If \p entity is considered absent according to 15.5.2.12 point 1.,
1806 /// the returned value is an absent fir.box, otherwise it is a fir.box
1807 /// describing \p entity.
1808 static ExvAndCleanup genOptionalBox(fir::FirOpBuilder &builder,
1809                                     mlir::Location loc, hlfir::Entity entity,
1810                                     mlir::Value isPresent) {
1811   auto [exv, cleanup] = hlfir::translateToExtendedValue(loc, builder, entity);
1812 
1813   // Non allocatable/pointer optional box -> simply forward
1814   if (exv.getBoxOf<fir::BoxValue>())
1815     return {exv, cleanup};
1816 
1817   fir::ExtendedValue newExv = exv;
1818   // Optional allocatable/pointer -> Cannot be absent, but need to translate
1819   // unallocated/diassociated into absent fir.box.
1820   if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>())
1821     newExv = fir::factory::genMutableBoxRead(builder, loc, *box);
1822 
1823   // createBox will not do create any invalid memory dereferences if exv is
1824   // absent. The created fir.box will not be usable, but the SelectOp below
1825   // ensures it won't be.
1826   mlir::Value box = builder.createBox(loc, newExv);
1827   mlir::Type boxType = box.getType();
1828   auto absent = builder.create<fir::AbsentOp>(loc, boxType);
1829   auto boxOrAbsent = builder.create<mlir::arith::SelectOp>(
1830       loc, boxType, isPresent, box, absent);
1831   return {fir::BoxValue(boxOrAbsent), cleanup};
1832 }
1833 
1834 /// Lower calls to intrinsic procedures with custom optional handling where the
1835 /// actual arguments have been pre-lowered
1836 static std::optional<hlfir::EntityWithAttributes> genCustomIntrinsicRefCore(
1837     Fortran::lower::PreparedActualArguments &loweredActuals,
1838     const Fortran::evaluate::SpecificIntrinsic *intrinsic,
1839     CallContext &callContext) {
1840   auto &builder = callContext.getBuilder();
1841   const auto &loc = callContext.loc;
1842   assert(intrinsic &&
1843          Fortran::lower::intrinsicRequiresCustomOptionalHandling(
1844              callContext.procRef, *intrinsic, callContext.converter));
1845 
1846   // helper to get a particular prepared argument
1847   auto getArgument = [&](std::size_t i, bool loadArg) -> fir::ExtendedValue {
1848     if (!loweredActuals[i])
1849       return fir::getAbsentIntrinsicArgument();
1850     hlfir::Entity actual = loweredActuals[i]->getActual(loc, builder);
1851     if (loadArg && fir::conformsWithPassByRef(actual.getType())) {
1852       return hlfir::loadTrivialScalar(loc, builder, actual);
1853     }
1854     return Fortran::lower::translateToExtendedValue(loc, builder, actual,
1855                                                     callContext.stmtCtx);
1856   };
1857   // helper to get the isPresent flag for a particular prepared argument
1858   auto isPresent = [&](std::size_t i) -> std::optional<mlir::Value> {
1859     if (!loweredActuals[i])
1860       return {builder.createBool(loc, false)};
1861     if (loweredActuals[i]->handleDynamicOptional())
1862       return {loweredActuals[i]->getIsPresent()};
1863     return std::nullopt;
1864   };
1865 
1866   assert(callContext.resultType &&
1867          "the elemental intrinsics with custom handling are all functions");
1868   // if callContext.resultType is an array then this was originally an elemental
1869   // call. What we are lowering here is inside the kernel of the hlfir.elemental
1870   // so we should return the scalar type. If the return type is already a scalar
1871   // then it should be unchanged here.
1872   mlir::Type resTy = hlfir::getFortranElementType(*callContext.resultType);
1873   fir::ExtendedValue result = Fortran::lower::lowerCustomIntrinsic(
1874       builder, loc, callContext.getProcedureName(), resTy, isPresent,
1875       getArgument, loweredActuals.size(), callContext.stmtCtx);
1876 
1877   return {hlfir::EntityWithAttributes{extendedValueToHlfirEntity(
1878       loc, builder, result, ".tmp.custom_intrinsic_result")}};
1879 }
1880 
1881 /// Lower calls to intrinsic procedures with actual arguments that have been
1882 /// pre-lowered but have not yet been prepared according to the interface.
1883 static std::optional<hlfir::EntityWithAttributes>
1884 genIntrinsicRefCore(Fortran::lower::PreparedActualArguments &loweredActuals,
1885                     const Fortran::evaluate::SpecificIntrinsic *intrinsic,
1886                     const fir::IntrinsicHandlerEntry &intrinsicEntry,
1887                     CallContext &callContext) {
1888   auto &converter = callContext.converter;
1889   if (intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling(
1890                        callContext.procRef, *intrinsic, converter))
1891     return genCustomIntrinsicRefCore(loweredActuals, intrinsic, callContext);
1892   llvm::SmallVector<fir::ExtendedValue> operands;
1893   llvm::SmallVector<hlfir::CleanupFunction> cleanupFns;
1894   auto addToCleanups = [&cleanupFns](std::optional<hlfir::CleanupFunction> fn) {
1895     if (fn)
1896       cleanupFns.emplace_back(std::move(*fn));
1897   };
1898   auto &stmtCtx = callContext.stmtCtx;
1899   fir::FirOpBuilder &builder = callContext.getBuilder();
1900   mlir::Location loc = callContext.loc;
1901   const fir::IntrinsicArgumentLoweringRules *argLowering =
1902       intrinsicEntry.getArgumentLoweringRules();
1903   for (auto arg : llvm::enumerate(loweredActuals)) {
1904     if (!arg.value()) {
1905       operands.emplace_back(fir::getAbsentIntrinsicArgument());
1906       continue;
1907     }
1908     if (!argLowering) {
1909       // No argument lowering instruction, lower by value.
1910       assert(!arg.value()->handleDynamicOptional() &&
1911              "should use genOptionalValue");
1912       hlfir::Entity actual = arg.value()->getActual(loc, builder);
1913       operands.emplace_back(
1914           Fortran::lower::convertToValue(loc, converter, actual, stmtCtx));
1915       continue;
1916     }
1917     // Helper to get the type of the Fortran expression in case it is a
1918     // computed value that must be placed in memory (logicals are computed as
1919     // i1, but must be placed in memory as fir.logical).
1920     auto getActualFortranElementType = [&]() -> mlir::Type {
1921       if (const Fortran::lower::SomeExpr *expr =
1922               callContext.procRef.UnwrapArgExpr(arg.index())) {
1923 
1924         mlir::Type type = converter.genType(*expr);
1925         return hlfir::getFortranElementType(type);
1926       }
1927       // TYPE(*): is already in memory anyway. Can return none
1928       // here.
1929       return builder.getNoneType();
1930     };
1931     // Ad-hoc argument lowering handling.
1932     fir::ArgLoweringRule argRules =
1933         fir::lowerIntrinsicArgumentAs(*argLowering, arg.index());
1934     if (arg.value()->handleDynamicOptional()) {
1935       mlir::Value isPresent = arg.value()->getIsPresent();
1936       switch (argRules.lowerAs) {
1937       case fir::LowerIntrinsicArgAs::Value: {
1938         // In case of elemental call, getActual() may produce
1939         // a designator denoting the array element to be passed
1940         // to the subprogram. If the actual array is dynamically
1941         // optional the designator must be generated under
1942         // isPresent check, because the box bounds reads will be
1943         // generated in the codegen. These reads are illegal,
1944         // if the dynamically optional argument is absent.
1945         auto getActualCb = [&](mlir::Location loc,
1946                                fir::FirOpBuilder &builder) -> hlfir::Entity {
1947           return arg.value()->getActual(loc, builder);
1948         };
1949         auto [exv, cleanup] =
1950             genOptionalValue(builder, loc, getActualFortranElementType(),
1951                              getActualCb, isPresent);
1952         addToCleanups(std::move(cleanup));
1953         operands.emplace_back(exv);
1954         continue;
1955       }
1956       case fir::LowerIntrinsicArgAs::Addr: {
1957         hlfir::Entity actual = arg.value()->getActual(loc, builder);
1958         auto [exv, cleanup] = genOptionalAddr(builder, loc, actual, isPresent);
1959         addToCleanups(std::move(cleanup));
1960         operands.emplace_back(exv);
1961         continue;
1962       }
1963       case fir::LowerIntrinsicArgAs::Box: {
1964         hlfir::Entity actual = arg.value()->getActual(loc, builder);
1965         auto [exv, cleanup] = genOptionalBox(builder, loc, actual, isPresent);
1966         addToCleanups(std::move(cleanup));
1967         operands.emplace_back(exv);
1968         continue;
1969       }
1970       case fir::LowerIntrinsicArgAs::Inquired: {
1971         hlfir::Entity actual = arg.value()->getActual(loc, builder);
1972         auto [exv, cleanup] =
1973             hlfir::translateToExtendedValue(loc, builder, actual);
1974         addToCleanups(std::move(cleanup));
1975         operands.emplace_back(exv);
1976         continue;
1977       }
1978       }
1979       llvm_unreachable("bad switch");
1980     }
1981 
1982     hlfir::Entity actual = arg.value()->getActual(loc, builder);
1983     switch (argRules.lowerAs) {
1984     case fir::LowerIntrinsicArgAs::Value:
1985       operands.emplace_back(
1986           Fortran::lower::convertToValue(loc, converter, actual, stmtCtx));
1987       continue;
1988     case fir::LowerIntrinsicArgAs::Addr:
1989       operands.emplace_back(Fortran::lower::convertToAddress(
1990           loc, converter, actual, stmtCtx, getActualFortranElementType()));
1991       continue;
1992     case fir::LowerIntrinsicArgAs::Box:
1993       operands.emplace_back(Fortran::lower::convertToBox(
1994           loc, converter, actual, stmtCtx, getActualFortranElementType()));
1995       continue;
1996     case fir::LowerIntrinsicArgAs::Inquired:
1997       if (const Fortran::lower::SomeExpr *expr =
1998               callContext.procRef.UnwrapArgExpr(arg.index())) {
1999         if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(
2000                 *expr)) {
2001           // NULL() pointer without a MOLD must be passed as a deallocated
2002           // pointer (see table 16.5 in Fortran 2018 standard).
2003           // !fir.box<!fir.ptr<none>> should always be valid in this context.
2004           mlir::Type noneTy = mlir::NoneType::get(builder.getContext());
2005           mlir::Type nullPtrTy = fir::PointerType::get(noneTy);
2006           mlir::Type boxTy = fir::BoxType::get(nullPtrTy);
2007           mlir::Value boxStorage =
2008               fir::factory::genNullBoxStorage(builder, loc, boxTy);
2009           hlfir::EntityWithAttributes nullBoxEntity =
2010               extendedValueToHlfirEntity(loc, builder, boxStorage,
2011                                          ".tmp.null_box");
2012           operands.emplace_back(Fortran::lower::translateToExtendedValue(
2013               loc, builder, nullBoxEntity, stmtCtx));
2014           continue;
2015         }
2016       }
2017       // Place hlfir.expr in memory, and unbox fir.boxchar. Other entities
2018       // are translated to fir::ExtendedValue without transformation (notably,
2019       // pointers/allocatable are not dereferenced).
2020       // TODO: once lowering to FIR retires, UBOUND and LBOUND can be simplified
2021       // since the fir.box lowered here are now guaranteed to contain the local
2022       // lower bounds thanks to the hlfir.declare (the extra rebox can be
2023       // removed).
2024       operands.emplace_back(Fortran::lower::translateToExtendedValue(
2025           loc, builder, actual, stmtCtx));
2026       continue;
2027     }
2028     llvm_unreachable("bad switch");
2029   }
2030   // genIntrinsicCall needs the scalar type, even if this is a transformational
2031   // procedure returning an array.
2032   std::optional<mlir::Type> scalarResultType;
2033   if (callContext.resultType)
2034     scalarResultType = hlfir::getFortranElementType(*callContext.resultType);
2035   const std::string intrinsicName = callContext.getProcedureName();
2036   // Let the intrinsic library lower the intrinsic procedure call.
2037   auto [resultExv, mustBeFreed] = genIntrinsicCall(
2038       builder, loc, intrinsicEntry, scalarResultType, operands, &converter);
2039   for (const hlfir::CleanupFunction &fn : cleanupFns)
2040     fn();
2041   if (!fir::getBase(resultExv))
2042     return std::nullopt;
2043   hlfir::EntityWithAttributes resultEntity = extendedValueToHlfirEntity(
2044       loc, builder, resultExv, ".tmp.intrinsic_result");
2045   // Move result into memory into an hlfir.expr since they are immutable from
2046   // that point, and the result storage is some temp. "Null" is special: it
2047   // returns a null pointer variable that should not be transformed into a value
2048   // (what matters is the memory address).
2049   if (resultEntity.isVariable() && intrinsicName != "null") {
2050     assert(!fir::isa_trivial(fir::unwrapRefType(resultEntity.getType())) &&
2051            "expect intrinsic scalar results to not be in memory");
2052     hlfir::AsExprOp asExpr;
2053     // Character/Derived MERGE lowering returns one of its argument address
2054     // (this is the only intrinsic implemented in that way so far). The
2055     // ownership of this address cannot be taken here since it may not be a
2056     // temp.
2057     if (intrinsicName == "merge")
2058       asExpr = builder.create<hlfir::AsExprOp>(loc, resultEntity);
2059     else
2060       asExpr = builder.create<hlfir::AsExprOp>(
2061           loc, resultEntity, builder.createBool(loc, mustBeFreed));
2062     resultEntity = hlfir::EntityWithAttributes{asExpr.getResult()};
2063   }
2064   return resultEntity;
2065 }
2066 
2067 /// Lower calls to intrinsic procedures with actual arguments that have been
2068 /// pre-lowered but have not yet been prepared according to the interface.
2069 static std::optional<hlfir::EntityWithAttributes> genHLFIRIntrinsicRefCore(
2070     Fortran::lower::PreparedActualArguments &loweredActuals,
2071     const Fortran::evaluate::SpecificIntrinsic *intrinsic,
2072     const fir::IntrinsicHandlerEntry &intrinsicEntry,
2073     CallContext &callContext) {
2074   // Try lowering transformational intrinsic ops to HLFIR ops if enabled
2075   // (transformational always have a result type)
2076   if (useHlfirIntrinsicOps && callContext.resultType) {
2077     fir::FirOpBuilder &builder = callContext.getBuilder();
2078     mlir::Location loc = callContext.loc;
2079     const std::string intrinsicName = callContext.getProcedureName();
2080     const fir::IntrinsicArgumentLoweringRules *argLowering =
2081         intrinsicEntry.getArgumentLoweringRules();
2082     std::optional<hlfir::EntityWithAttributes> res =
2083         Fortran::lower::lowerHlfirIntrinsic(builder, loc, intrinsicName,
2084                                             loweredActuals, argLowering,
2085                                             *callContext.resultType);
2086     if (res)
2087       return res;
2088   }
2089 
2090   // fallback to calling the intrinsic via fir.call
2091   return genIntrinsicRefCore(loweredActuals, intrinsic, intrinsicEntry,
2092                              callContext);
2093 }
2094 
2095 namespace {
2096 template <typename ElementalCallBuilderImpl>
2097 class ElementalCallBuilder {
2098 public:
2099   std::optional<hlfir::EntityWithAttributes>
2100   genElementalCall(Fortran::lower::PreparedActualArguments &loweredActuals,
2101                    bool isImpure, CallContext &callContext) {
2102     mlir::Location loc = callContext.loc;
2103     fir::FirOpBuilder &builder = callContext.getBuilder();
2104     unsigned numArgs = loweredActuals.size();
2105     // Step 1: dereference pointers/allocatables and compute elemental shape.
2106     mlir::Value shape;
2107     Fortran::lower::PreparedActualArgument *optionalWithShape;
2108     // 10.1.4 p5. Impure elemental procedures must be called in element order.
2109     bool mustBeOrdered = isImpure;
2110     for (unsigned i = 0; i < numArgs; ++i) {
2111       auto &preparedActual = loweredActuals[i];
2112       if (preparedActual) {
2113         // Elemental procedure dummy arguments cannot be pointer/allocatables
2114         // (C15100), so it is safe to dereference any pointer or allocatable
2115         // actual argument now instead of doing this inside the elemental
2116         // region.
2117         preparedActual->derefPointersAndAllocatables(loc, builder);
2118         // Better to load scalars outside of the loop when possible.
2119         if (!preparedActual->handleDynamicOptional() &&
2120             impl().canLoadActualArgumentBeforeLoop(i))
2121           preparedActual->loadTrivialScalar(loc, builder);
2122         // TODO: merge shape instead of using the first one.
2123         if (!shape && preparedActual->isArray()) {
2124           if (preparedActual->handleDynamicOptional())
2125             optionalWithShape = &*preparedActual;
2126           else
2127             shape = preparedActual->genShape(loc, builder);
2128         }
2129         // 15.8.3 p1. Elemental procedure with intent(out)/intent(inout)
2130         // arguments must be called in element order.
2131         if (impl().argMayBeModifiedByCall(i))
2132           mustBeOrdered = true;
2133       }
2134     }
2135     if (!shape && optionalWithShape) {
2136       // If all array operands appear in optional positions, then none of them
2137       // is allowed to be absent as per 15.5.2.12 point 3. (6). Just pick the
2138       // first operand.
2139       shape = optionalWithShape->genShape(loc, builder);
2140       // TODO: There is an opportunity to add a runtime check here that
2141       // this array is present as required. Also, the optionality of all actual
2142       // could be checked and reset given the Fortran requirement.
2143       optionalWithShape->resetOptionalAspect();
2144     }
2145     assert(shape &&
2146            "elemental array calls must have at least one array arguments");
2147 
2148     // Evaluate the actual argument array expressions before the elemental
2149     // call of an impure subprogram or a subprogram with intent(out) or
2150     // intent(inout) arguments. Note that the scalar arguments are handled
2151     // above.
2152     if (mustBeOrdered) {
2153       for (auto &preparedActual : loweredActuals) {
2154         if (preparedActual) {
2155           if (hlfir::AssociateOp associate =
2156                   preparedActual->associateIfArrayExpr(loc, builder)) {
2157             fir::FirOpBuilder *bldr = &builder;
2158             callContext.stmtCtx.attachCleanup(
2159                 [=]() { bldr->create<hlfir::EndAssociateOp>(loc, associate); });
2160           }
2161         }
2162       }
2163     }
2164 
2165     // Push a new local scope so that any temps made inside the elemental
2166     // iterations are cleaned up inside the iterations.
2167     if (!callContext.resultType) {
2168       // Subroutine case. Generate call inside loop nest.
2169       hlfir::LoopNest loopNest =
2170           hlfir::genLoopNest(loc, builder, shape, !mustBeOrdered);
2171       mlir::ValueRange oneBasedIndices = loopNest.oneBasedIndices;
2172       auto insPt = builder.saveInsertionPoint();
2173       builder.setInsertionPointToStart(loopNest.body);
2174       callContext.stmtCtx.pushScope();
2175       for (auto &preparedActual : loweredActuals)
2176         if (preparedActual)
2177           preparedActual->setElementalIndices(oneBasedIndices);
2178       impl().genElementalKernel(loweredActuals, callContext);
2179       callContext.stmtCtx.finalizeAndPop();
2180       builder.restoreInsertionPoint(insPt);
2181       return std::nullopt;
2182     }
2183     // Function case: generate call inside hlfir.elemental
2184     mlir::Type elementType =
2185         hlfir::getFortranElementType(*callContext.resultType);
2186     // Get result length parameters.
2187     llvm::SmallVector<mlir::Value> typeParams;
2188     if (mlir::isa<fir::CharacterType>(elementType) ||
2189         fir::isRecordWithTypeParameters(elementType)) {
2190       auto charType = mlir::dyn_cast<fir::CharacterType>(elementType);
2191       if (charType && charType.hasConstantLen())
2192         typeParams.push_back(builder.createIntegerConstant(
2193             loc, builder.getIndexType(), charType.getLen()));
2194       else if (charType)
2195         typeParams.push_back(impl().computeDynamicCharacterResultLength(
2196             loweredActuals, callContext));
2197       else
2198         TODO(
2199             loc,
2200             "compute elemental PDT function result length parameters in HLFIR");
2201     }
2202     auto genKernel = [&](mlir::Location l, fir::FirOpBuilder &b,
2203                          mlir::ValueRange oneBasedIndices) -> hlfir::Entity {
2204       callContext.stmtCtx.pushScope();
2205       for (auto &preparedActual : loweredActuals)
2206         if (preparedActual)
2207           preparedActual->setElementalIndices(oneBasedIndices);
2208       auto res = *impl().genElementalKernel(loweredActuals, callContext);
2209       callContext.stmtCtx.finalizeAndPop();
2210       // Note that an hlfir.destroy is not emitted for the result since it
2211       // is still used by the hlfir.yield_element that also marks its last
2212       // use.
2213       return res;
2214     };
2215     mlir::Value polymorphicMold;
2216     if (fir::isPolymorphicType(*callContext.resultType))
2217       polymorphicMold =
2218           impl().getPolymorphicResultMold(loweredActuals, callContext);
2219     mlir::Value elemental =
2220         hlfir::genElementalOp(loc, builder, elementType, shape, typeParams,
2221                               genKernel, !mustBeOrdered, polymorphicMold);
2222     // If the function result requires finalization, then it has to be done
2223     // for the array result of the elemental call. We have to communicate
2224     // this via the DestroyOp's attribute.
2225     bool mustFinalizeExpr = impl().resultMayRequireFinalization(callContext);
2226     fir::FirOpBuilder *bldr = &builder;
2227     callContext.stmtCtx.attachCleanup([=]() {
2228       bldr->create<hlfir::DestroyOp>(loc, elemental, mustFinalizeExpr);
2229     });
2230     return hlfir::EntityWithAttributes{elemental};
2231   }
2232 
2233 private:
2234   ElementalCallBuilderImpl &impl() {
2235     return *static_cast<ElementalCallBuilderImpl *>(this);
2236   }
2237 };
2238 
2239 class ElementalUserCallBuilder
2240     : public ElementalCallBuilder<ElementalUserCallBuilder> {
2241 public:
2242   ElementalUserCallBuilder(Fortran::lower::CallerInterface &caller,
2243                            mlir::FunctionType callSiteType)
2244       : caller{caller}, callSiteType{callSiteType} {}
2245   std::optional<hlfir::Entity>
2246   genElementalKernel(Fortran::lower::PreparedActualArguments &loweredActuals,
2247                      CallContext &callContext) {
2248     return genUserCall(loweredActuals, caller, callSiteType, callContext);
2249   }
2250 
2251   bool argMayBeModifiedByCall(unsigned argIdx) const {
2252     assert(argIdx < caller.getPassedArguments().size() && "bad argument index");
2253     return caller.getPassedArguments()[argIdx].mayBeModifiedByCall();
2254   }
2255 
2256   bool canLoadActualArgumentBeforeLoop(unsigned argIdx) const {
2257     using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
2258     const auto &passedArgs{caller.getPassedArguments()};
2259     assert(argIdx < passedArgs.size() && "bad argument index");
2260     // If the actual argument does not need to be passed via an address,
2261     // or will be passed in the address of a temporary copy, it can be loaded
2262     // before the elemental loop nest.
2263     const auto &arg{passedArgs[argIdx]};
2264     return arg.passBy == PassBy::Value ||
2265            arg.passBy == PassBy::BaseAddressValueAttribute;
2266   }
2267 
2268   mlir::Value computeDynamicCharacterResultLength(
2269       Fortran::lower::PreparedActualArguments &loweredActuals,
2270       CallContext &callContext) {
2271     fir::FirOpBuilder &builder = callContext.getBuilder();
2272     mlir::Location loc = callContext.loc;
2273     auto &converter = callContext.converter;
2274     mlir::Type idxTy = builder.getIndexType();
2275     llvm::SmallVector<CallCleanUp> callCleanUps;
2276 
2277     prepareUserCallArguments(loweredActuals, caller, callSiteType, callContext,
2278                              callCleanUps);
2279 
2280     callContext.symMap.pushScope();
2281 
2282     // Map prepared argument to dummy symbol to be able to lower spec expr.
2283     for (const auto &arg : caller.getPassedArguments()) {
2284       const Fortran::semantics::Symbol *sym = caller.getDummySymbol(arg);
2285       assert(sym && "expect symbol for dummy argument");
2286       auto input = caller.getInput(arg);
2287       fir::ExtendedValue exv = Fortran::lower::translateToExtendedValue(
2288           loc, builder, hlfir::Entity{input}, callContext.stmtCtx);
2289       fir::FortranVariableOpInterface variableIface = hlfir::genDeclare(
2290           loc, builder, exv, "dummy.tmp", fir::FortranVariableFlagsAttr{});
2291       callContext.symMap.addVariableDefinition(*sym, variableIface);
2292     }
2293 
2294     auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value {
2295       mlir::Value convertExpr = builder.createConvert(
2296           loc, idxTy,
2297           fir::getBase(converter.genExprValue(expr, callContext.stmtCtx)));
2298       return fir::factory::genMaxWithZero(builder, loc, convertExpr);
2299     };
2300 
2301     llvm::SmallVector<mlir::Value> lengths;
2302     caller.walkResultLengths(
2303         [&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) {
2304           assert(!isAssumedSizeExtent && "result cannot be assumed-size");
2305           lengths.emplace_back(lowerSpecExpr(e));
2306         });
2307     callContext.symMap.popScope();
2308     assert(lengths.size() == 1 && "expect 1 length parameter for the result");
2309     return lengths[0];
2310   }
2311 
2312   mlir::Value getPolymorphicResultMold(
2313       Fortran::lower::PreparedActualArguments &loweredActuals,
2314       CallContext &callContext) {
2315     fir::emitFatalError(callContext.loc,
2316                         "elemental function call with polymorphic result");
2317     return {};
2318   }
2319 
2320   bool resultMayRequireFinalization(CallContext &callContext) const {
2321     std::optional<Fortran::evaluate::DynamicType> retTy =
2322         caller.getCallDescription().proc().GetType();
2323     if (!retTy)
2324       return false;
2325 
2326     if (retTy->IsPolymorphic() || retTy->IsUnlimitedPolymorphic())
2327       fir::emitFatalError(
2328           callContext.loc,
2329           "elemental function call with [unlimited-]polymorphic result");
2330 
2331     if (retTy->category() == Fortran::common::TypeCategory::Derived) {
2332       const Fortran::semantics::DerivedTypeSpec &typeSpec =
2333           retTy->GetDerivedTypeSpec();
2334       return Fortran::semantics::IsFinalizable(typeSpec);
2335     }
2336 
2337     return false;
2338   }
2339 
2340 private:
2341   Fortran::lower::CallerInterface &caller;
2342   mlir::FunctionType callSiteType;
2343 };
2344 
2345 class ElementalIntrinsicCallBuilder
2346     : public ElementalCallBuilder<ElementalIntrinsicCallBuilder> {
2347 public:
2348   ElementalIntrinsicCallBuilder(
2349       const Fortran::evaluate::SpecificIntrinsic *intrinsic,
2350       const fir::IntrinsicHandlerEntry &intrinsicEntry, bool isFunction)
2351       : intrinsic{intrinsic}, intrinsicEntry{intrinsicEntry},
2352         isFunction{isFunction} {}
2353   std::optional<hlfir::Entity>
2354   genElementalKernel(Fortran::lower::PreparedActualArguments &loweredActuals,
2355                      CallContext &callContext) {
2356     return genHLFIRIntrinsicRefCore(loweredActuals, intrinsic, intrinsicEntry,
2357                                     callContext);
2358   }
2359   // Elemental intrinsic functions cannot modify their arguments.
2360   bool argMayBeModifiedByCall(int) const { return !isFunction; }
2361   bool canLoadActualArgumentBeforeLoop(int) const {
2362     // Elemental intrinsic functions never need the actual addresses
2363     // of their arguments.
2364     return isFunction;
2365   }
2366 
2367   mlir::Value computeDynamicCharacterResultLength(
2368       Fortran::lower::PreparedActualArguments &loweredActuals,
2369       CallContext &callContext) {
2370     if (intrinsic)
2371       if (intrinsic->name == "adjustr" || intrinsic->name == "adjustl" ||
2372           intrinsic->name == "merge")
2373         return loweredActuals[0].value().genCharLength(
2374             callContext.loc, callContext.getBuilder());
2375     // Character MIN/MAX is the min/max of the arguments length that are
2376     // present.
2377     TODO(callContext.loc,
2378          "compute elemental character min/max function result length in HLFIR");
2379   }
2380 
2381   mlir::Value getPolymorphicResultMold(
2382       Fortran::lower::PreparedActualArguments &loweredActuals,
2383       CallContext &callContext) {
2384     if (!intrinsic)
2385       return {};
2386 
2387     if (intrinsic->name == "merge") {
2388       // MERGE seems to be the only elemental function that can produce
2389       // polymorphic result. The MERGE's result is polymorphic iff
2390       // both TSOURCE and FSOURCE are polymorphic, and they also must have
2391       // the same declared and dynamic types. So any of them can be used
2392       // for the mold.
2393       assert(!loweredActuals.empty());
2394       return loweredActuals.front()->getPolymorphicMold(callContext.loc);
2395     }
2396 
2397     return {};
2398   }
2399 
2400   bool resultMayRequireFinalization(
2401       [[maybe_unused]] CallContext &callContext) const {
2402     // FIXME: need access to the CallerInterface's return type
2403     // to check if the result may need finalization (e.g. the result
2404     // of MERGE).
2405     return false;
2406   }
2407 
2408 private:
2409   const Fortran::evaluate::SpecificIntrinsic *intrinsic;
2410   fir::IntrinsicHandlerEntry intrinsicEntry;
2411   const bool isFunction;
2412 };
2413 } // namespace
2414 
2415 static std::optional<mlir::Value>
2416 genIsPresentIfArgMaybeAbsent(mlir::Location loc, hlfir::Entity actual,
2417                              const Fortran::lower::SomeExpr &expr,
2418                              CallContext &callContext,
2419                              bool passAsAllocatableOrPointer) {
2420   if (!Fortran::evaluate::MayBePassedAsAbsentOptional(expr))
2421     return std::nullopt;
2422   fir::FirOpBuilder &builder = callContext.getBuilder();
2423   if (!passAsAllocatableOrPointer &&
2424       Fortran::evaluate::IsAllocatableOrPointerObject(expr)) {
2425     // Passing Allocatable/Pointer to non-pointer/non-allocatable OPTIONAL.
2426     // Fortran 2018 15.5.2.12 point 1: If unallocated/disassociated, it is
2427     // as if the argument was absent. The main care here is to not do a
2428     // copy-in/copy-out because the temp address, even though pointing to a
2429     // null size storage, would not be a nullptr and therefore the argument
2430     // would not be considered absent on the callee side. Note: if the
2431     // allocatable/pointer is also optional, it cannot be absent as per
2432     // 15.5.2.12 point 7. and 8. We rely on this to un-conditionally read
2433     // the allocatable/pointer descriptor here.
2434     mlir::Value addr = genVariableRawAddress(loc, builder, actual);
2435     return builder.genIsNotNullAddr(loc, addr);
2436   }
2437   // TODO: what if passing allocatable target to optional intent(in) pointer?
2438   // May fall into the category above if the allocatable is not optional.
2439 
2440   // Passing an optional to an optional.
2441   return builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), actual)
2442       .getResult();
2443 }
2444 
2445 // Lower a reference to an elemental intrinsic procedure with array arguments
2446 // and custom optional handling
2447 static std::optional<hlfir::EntityWithAttributes>
2448 genCustomElementalIntrinsicRef(
2449     const Fortran::evaluate::SpecificIntrinsic *intrinsic,
2450     CallContext &callContext) {
2451   assert(callContext.isElementalProcWithArrayArgs() &&
2452          "Use genCustomIntrinsicRef for scalar calls");
2453   mlir::Location loc = callContext.loc;
2454   auto &converter = callContext.converter;
2455   Fortran::lower::PreparedActualArguments operands;
2456   assert(intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling(
2457                           callContext.procRef, *intrinsic, converter));
2458 
2459   // callback for optional arguments
2460   auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) {
2461     hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR(
2462         loc, converter, expr, callContext.symMap, callContext.stmtCtx);
2463     std::optional<mlir::Value> isPresent =
2464         genIsPresentIfArgMaybeAbsent(loc, actual, expr, callContext,
2465                                      /*passAsAllocatableOrPointer=*/false);
2466     operands.emplace_back(
2467         Fortran::lower::PreparedActualArgument{actual, isPresent});
2468   };
2469 
2470   // callback for non-optional arguments
2471   auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr,
2472                              fir::LowerIntrinsicArgAs lowerAs) {
2473     hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR(
2474         loc, converter, expr, callContext.symMap, callContext.stmtCtx);
2475     operands.emplace_back(Fortran::lower::PreparedActualArgument{
2476         actual, /*isPresent=*/std::nullopt});
2477   };
2478 
2479   Fortran::lower::prepareCustomIntrinsicArgument(
2480       callContext.procRef, *intrinsic, callContext.resultType,
2481       prepareOptionalArg, prepareOtherArg, converter);
2482 
2483   std::optional<fir::IntrinsicHandlerEntry> intrinsicEntry =
2484       fir::lookupIntrinsicHandler(callContext.getBuilder(),
2485                                   callContext.getProcedureName(),
2486                                   callContext.resultType);
2487   assert(intrinsicEntry.has_value() &&
2488          "intrinsic with custom handling for OPTIONAL arguments must have "
2489          "lowering entries");
2490   // All of the custom intrinsic elementals with custom handling are pure
2491   // functions
2492   return ElementalIntrinsicCallBuilder{intrinsic, *intrinsicEntry,
2493                                        /*isFunction=*/true}
2494       .genElementalCall(operands, /*isImpure=*/false, callContext);
2495 }
2496 
2497 // Lower a reference to an intrinsic procedure with custom optional handling
2498 static std::optional<hlfir::EntityWithAttributes>
2499 genCustomIntrinsicRef(const Fortran::evaluate::SpecificIntrinsic *intrinsic,
2500                       CallContext &callContext) {
2501   assert(!callContext.isElementalProcWithArrayArgs() &&
2502          "Needs to be run through ElementalIntrinsicCallBuilder first");
2503   mlir::Location loc = callContext.loc;
2504   fir::FirOpBuilder &builder = callContext.getBuilder();
2505   auto &converter = callContext.converter;
2506   auto &stmtCtx = callContext.stmtCtx;
2507   assert(intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling(
2508                           callContext.procRef, *intrinsic, converter));
2509   Fortran::lower::PreparedActualArguments loweredActuals;
2510 
2511   // callback for optional arguments
2512   auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) {
2513     hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR(
2514         loc, converter, expr, callContext.symMap, callContext.stmtCtx);
2515     mlir::Value isPresent =
2516         genIsPresentIfArgMaybeAbsent(loc, actual, expr, callContext,
2517                                      /*passAsAllocatableOrPointer*/ false)
2518             .value();
2519     loweredActuals.emplace_back(
2520         Fortran::lower::PreparedActualArgument{actual, {isPresent}});
2521   };
2522 
2523   // callback for non-optional arguments
2524   auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr,
2525                              fir::LowerIntrinsicArgAs lowerAs) {
2526     auto getActualFortranElementType = [&]() -> mlir::Type {
2527       return hlfir::getFortranElementType(converter.genType(expr));
2528     };
2529     hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR(
2530         loc, converter, expr, callContext.symMap, callContext.stmtCtx);
2531     std::optional<fir::ExtendedValue> exv;
2532     switch (lowerAs) {
2533     case fir::LowerIntrinsicArgAs::Value:
2534       exv = Fortran::lower::convertToValue(loc, converter, actual, stmtCtx);
2535       break;
2536     case fir::LowerIntrinsicArgAs::Addr:
2537       exv = Fortran::lower::convertToAddress(loc, converter, actual, stmtCtx,
2538                                              getActualFortranElementType());
2539       break;
2540     case fir::LowerIntrinsicArgAs::Box:
2541       exv = Fortran::lower::convertToBox(loc, converter, actual, stmtCtx,
2542                                          getActualFortranElementType());
2543       break;
2544     case fir::LowerIntrinsicArgAs::Inquired:
2545       exv = Fortran::lower::translateToExtendedValue(loc, builder, actual,
2546                                                      stmtCtx);
2547       break;
2548     }
2549     if (!exv)
2550       llvm_unreachable("bad switch");
2551     actual = extendedValueToHlfirEntity(loc, builder, exv.value(),
2552                                         "tmp.custom_intrinsic_arg");
2553     loweredActuals.emplace_back(Fortran::lower::PreparedActualArgument{
2554         actual, /*isPresent=*/std::nullopt});
2555   };
2556 
2557   Fortran::lower::prepareCustomIntrinsicArgument(
2558       callContext.procRef, *intrinsic, callContext.resultType,
2559       prepareOptionalArg, prepareOtherArg, converter);
2560 
2561   return genCustomIntrinsicRefCore(loweredActuals, intrinsic, callContext);
2562 }
2563 
2564 /// Lower an intrinsic procedure reference.
2565 /// \p intrinsic is null if this is an intrinsic module procedure that must be
2566 /// lowered as if it were an intrinsic module procedure (like C_LOC which is a
2567 /// procedure from intrinsic module iso_c_binding). Otherwise, \p intrinsic
2568 /// must not be null.
2569 
2570 static std::optional<hlfir::EntityWithAttributes>
2571 genIntrinsicRef(const Fortran::evaluate::SpecificIntrinsic *intrinsic,
2572                 const fir::IntrinsicHandlerEntry &intrinsicEntry,
2573                 CallContext &callContext) {
2574   mlir::Location loc = callContext.loc;
2575   Fortran::lower::PreparedActualArguments loweredActuals;
2576   const fir::IntrinsicArgumentLoweringRules *argLowering =
2577       intrinsicEntry.getArgumentLoweringRules();
2578   for (const auto &arg : llvm::enumerate(callContext.procRef.arguments())) {
2579 
2580     if (!arg.value()) {
2581       // Absent optional.
2582       loweredActuals.push_back(std::nullopt);
2583       continue;
2584     }
2585     auto *expr =
2586         Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg.value());
2587     if (!expr) {
2588       // TYPE(*) dummy. They are only allowed as argument of a few intrinsics
2589       // that do not take optional arguments: see Fortran 2018 standard C710.
2590       const Fortran::evaluate::Symbol *assumedTypeSym =
2591           arg.value()->GetAssumedTypeDummy();
2592       if (!assumedTypeSym)
2593         fir::emitFatalError(loc,
2594                             "expected assumed-type symbol as actual argument");
2595       std::optional<fir::FortranVariableOpInterface> var =
2596           callContext.symMap.lookupVariableDefinition(*assumedTypeSym);
2597       if (!var)
2598         fir::emitFatalError(loc, "assumed-type symbol was not lowered");
2599       assert(
2600           (!argLowering ||
2601            !fir::lowerIntrinsicArgumentAs(*argLowering, arg.index())
2602                 .handleDynamicOptional) &&
2603           "TYPE(*) are not expected to appear as optional intrinsic arguments");
2604       loweredActuals.push_back(Fortran::lower::PreparedActualArgument{
2605           hlfir::Entity{*var}, /*isPresent=*/std::nullopt});
2606       continue;
2607     }
2608     // arguments of bitwise comparison functions may not have nsw flag
2609     // even if -fno-wrapv is enabled
2610     mlir::arith::IntegerOverflowFlags iofBackup{};
2611     auto isBitwiseComparison = [](const std::string intrinsicName) -> bool {
2612       if (intrinsicName == "bge" || intrinsicName == "bgt" ||
2613           intrinsicName == "ble" || intrinsicName == "blt")
2614         return true;
2615       return false;
2616     };
2617     if (isBitwiseComparison(callContext.getProcedureName())) {
2618       iofBackup = callContext.getBuilder().getIntegerOverflowFlags();
2619       callContext.getBuilder().setIntegerOverflowFlags(
2620           mlir::arith::IntegerOverflowFlags::none);
2621     }
2622     auto loweredActual = Fortran::lower::convertExprToHLFIR(
2623         loc, callContext.converter, *expr, callContext.symMap,
2624         callContext.stmtCtx);
2625     if (isBitwiseComparison(callContext.getProcedureName()))
2626       callContext.getBuilder().setIntegerOverflowFlags(iofBackup);
2627 
2628     std::optional<mlir::Value> isPresent;
2629     if (argLowering) {
2630       fir::ArgLoweringRule argRules =
2631           fir::lowerIntrinsicArgumentAs(*argLowering, arg.index());
2632       if (argRules.handleDynamicOptional)
2633         isPresent =
2634             genIsPresentIfArgMaybeAbsent(loc, loweredActual, *expr, callContext,
2635                                          /*passAsAllocatableOrPointer=*/false);
2636     }
2637     loweredActuals.push_back(
2638         Fortran::lower::PreparedActualArgument{loweredActual, isPresent});
2639   }
2640 
2641   if (callContext.isElementalProcWithArrayArgs()) {
2642     // All intrinsic elemental functions are pure.
2643     const bool isFunction = callContext.resultType.has_value();
2644     return ElementalIntrinsicCallBuilder{intrinsic, intrinsicEntry, isFunction}
2645         .genElementalCall(loweredActuals, /*isImpure=*/!isFunction,
2646                           callContext);
2647   }
2648   std::optional<hlfir::EntityWithAttributes> result = genHLFIRIntrinsicRefCore(
2649       loweredActuals, intrinsic, intrinsicEntry, callContext);
2650   if (result && mlir::isa<hlfir::ExprType>(result->getType())) {
2651     fir::FirOpBuilder *bldr = &callContext.getBuilder();
2652     callContext.stmtCtx.attachCleanup(
2653         [=]() { bldr->create<hlfir::DestroyOp>(loc, *result); });
2654   }
2655   return result;
2656 }
2657 
2658 static std::optional<hlfir::EntityWithAttributes>
2659 genIntrinsicRef(const Fortran::evaluate::SpecificIntrinsic *intrinsic,
2660                 CallContext &callContext) {
2661   mlir::Location loc = callContext.loc;
2662   auto &converter = callContext.converter;
2663   if (intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling(
2664                        callContext.procRef, *intrinsic, converter)) {
2665     if (callContext.isElementalProcWithArrayArgs())
2666       return genCustomElementalIntrinsicRef(intrinsic, callContext);
2667     return genCustomIntrinsicRef(intrinsic, callContext);
2668   }
2669   std::optional<fir::IntrinsicHandlerEntry> intrinsicEntry =
2670       fir::lookupIntrinsicHandler(callContext.getBuilder(),
2671                                   callContext.getProcedureName(),
2672                                   callContext.resultType);
2673   if (!intrinsicEntry)
2674     fir::crashOnMissingIntrinsic(loc, callContext.getProcedureName());
2675   return genIntrinsicRef(intrinsic, *intrinsicEntry, callContext);
2676 }
2677 
2678 /// Main entry point to lower procedure references, regardless of what they are.
2679 static std::optional<hlfir::EntityWithAttributes>
2680 genProcedureRef(CallContext &callContext) {
2681   mlir::Location loc = callContext.loc;
2682   fir::FirOpBuilder &builder = callContext.getBuilder();
2683   if (auto *intrinsic = callContext.procRef.proc().GetSpecificIntrinsic())
2684     return genIntrinsicRef(intrinsic, callContext);
2685   // Intercept non BIND(C) module procedure reference that have lowering
2686   // handlers defined for there name. Otherwise, lower them as user
2687   // procedure calls and expect the implementation to be part of
2688   // runtime libraries with the proper name mangling.
2689   if (Fortran::lower::isIntrinsicModuleProcRef(callContext.procRef) &&
2690       !callContext.isBindcCall())
2691     if (std::optional<fir::IntrinsicHandlerEntry> intrinsicEntry =
2692             fir::lookupIntrinsicHandler(builder, callContext.getProcedureName(),
2693                                         callContext.resultType))
2694       return genIntrinsicRef(nullptr, *intrinsicEntry, callContext);
2695 
2696   if (callContext.isStatementFunctionCall())
2697     return genStmtFunctionRef(loc, callContext.converter, callContext.symMap,
2698                               callContext.stmtCtx, callContext.procRef);
2699 
2700   Fortran::lower::CallerInterface caller(callContext.procRef,
2701                                          callContext.converter);
2702   mlir::FunctionType callSiteType = caller.genFunctionType();
2703   const bool isElemental = callContext.isElementalProcWithArrayArgs();
2704   Fortran::lower::PreparedActualArguments loweredActuals;
2705   // Lower the actual arguments
2706   for (const Fortran::lower::CallInterface<
2707            Fortran::lower::CallerInterface>::PassedEntity &arg :
2708        caller.getPassedArguments())
2709     if (const auto *actual = arg.entity) {
2710       const auto *expr = actual->UnwrapExpr();
2711       if (!expr) {
2712         // TYPE(*) actual argument.
2713         const Fortran::evaluate::Symbol *assumedTypeSym =
2714             actual->GetAssumedTypeDummy();
2715         if (!assumedTypeSym)
2716           fir::emitFatalError(
2717               loc, "expected assumed-type symbol as actual argument");
2718         std::optional<fir::FortranVariableOpInterface> var =
2719             callContext.symMap.lookupVariableDefinition(*assumedTypeSym);
2720         if (!var)
2721           fir::emitFatalError(loc, "assumed-type symbol was not lowered");
2722         hlfir::Entity actual{*var};
2723         std::optional<mlir::Value> isPresent;
2724         if (arg.isOptional()) {
2725           // Passing an optional TYPE(*) to an optional TYPE(*). Note that
2726           // TYPE(*) cannot be ALLOCATABLE/POINTER (C709) so there is no
2727           // need to cover the case of passing an ALLOCATABLE/POINTER to an
2728           // OPTIONAL.
2729           isPresent =
2730               builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), actual)
2731                   .getResult();
2732         }
2733         loweredActuals.push_back(Fortran::lower::PreparedActualArgument{
2734             hlfir::Entity{*var}, isPresent});
2735         continue;
2736       }
2737 
2738       if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(
2739               *expr)) {
2740         if ((arg.passBy !=
2741              Fortran::lower::CallerInterface::PassEntityBy::MutableBox) &&
2742             (arg.passBy !=
2743              Fortran::lower::CallerInterface::PassEntityBy::BoxProcRef)) {
2744           assert(
2745               arg.isOptional() &&
2746               "NULL must be passed only to pointer, allocatable, or OPTIONAL");
2747           // Trying to lower NULL() outside of any context would lead to
2748           // trouble. NULL() here is equivalent to not providing the
2749           // actual argument.
2750           loweredActuals.emplace_back(std::nullopt);
2751           continue;
2752         }
2753       }
2754 
2755       if (isElemental && !arg.hasValueAttribute() &&
2756           Fortran::evaluate::IsVariable(*expr) &&
2757           Fortran::evaluate::HasVectorSubscript(*expr)) {
2758         // Vector subscripted arguments are copied in calls, except in elemental
2759         // calls without VALUE attribute where Fortran 2018 15.5.2.4 point 21
2760         // does not apply and the address of each element must be passed.
2761         hlfir::ElementalAddrOp elementalAddr =
2762             Fortran::lower::convertVectorSubscriptedExprToElementalAddr(
2763                 loc, callContext.converter, *expr, callContext.symMap,
2764                 callContext.stmtCtx);
2765         loweredActuals.emplace_back(
2766             Fortran::lower::PreparedActualArgument{elementalAddr});
2767         continue;
2768       }
2769 
2770       auto loweredActual = Fortran::lower::convertExprToHLFIR(
2771           loc, callContext.converter, *expr, callContext.symMap,
2772           callContext.stmtCtx);
2773       std::optional<mlir::Value> isPresent;
2774       if (arg.isOptional())
2775         isPresent = genIsPresentIfArgMaybeAbsent(
2776             loc, loweredActual, *expr, callContext,
2777             arg.passBy ==
2778                 Fortran::lower::CallerInterface::PassEntityBy::MutableBox);
2779 
2780       loweredActuals.emplace_back(
2781           Fortran::lower::PreparedActualArgument{loweredActual, isPresent});
2782     } else {
2783       // Optional dummy argument for which there is no actual argument.
2784       loweredActuals.emplace_back(std::nullopt);
2785     }
2786   if (isElemental) {
2787     bool isImpure = false;
2788     if (const Fortran::semantics::Symbol *procSym =
2789             callContext.procRef.proc().GetSymbol())
2790       isImpure = !Fortran::semantics::IsPureProcedure(*procSym);
2791     return ElementalUserCallBuilder{caller, callSiteType}.genElementalCall(
2792         loweredActuals, isImpure, callContext);
2793   }
2794   return genUserCall(loweredActuals, caller, callSiteType, callContext);
2795 }
2796 
2797 hlfir::Entity Fortran::lower::PreparedActualArgument::getActual(
2798     mlir::Location loc, fir::FirOpBuilder &builder) const {
2799   if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual)) {
2800     if (oneBasedElementalIndices)
2801       return hlfir::getElementAt(loc, builder, *actualEntity,
2802                                  *oneBasedElementalIndices);
2803     return *actualEntity;
2804   }
2805   assert(oneBasedElementalIndices && "expect elemental context");
2806   hlfir::ElementalAddrOp elementalAddr =
2807       std::get<hlfir::ElementalAddrOp>(actual);
2808   mlir::IRMapping mapper;
2809   auto alwaysFalse = [](hlfir::ElementalOp) -> bool { return false; };
2810   mlir::Value addr = hlfir::inlineElementalOp(
2811       loc, builder, elementalAddr, *oneBasedElementalIndices, mapper,
2812       /*mustRecursivelyInline=*/alwaysFalse);
2813   assert(elementalAddr.getCleanup().empty() && "no clean-up expected");
2814   elementalAddr.erase();
2815   return hlfir::Entity{addr};
2816 }
2817 
2818 bool Fortran::lower::isIntrinsicModuleProcRef(
2819     const Fortran::evaluate::ProcedureRef &procRef) {
2820   const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol();
2821   if (!symbol)
2822     return false;
2823   const Fortran::semantics::Symbol *module =
2824       symbol->GetUltimate().owner().GetSymbol();
2825   return module && module->attrs().test(Fortran::semantics::Attr::INTRINSIC);
2826 }
2827 
2828 static bool isInWhereMaskedExpression(fir::FirOpBuilder &builder) {
2829   // The MASK of the outer WHERE is not masked itself.
2830   mlir::Operation *op = builder.getRegion().getParentOp();
2831   return op && op->getParentOfType<hlfir::WhereOp>();
2832 }
2833 
2834 std::optional<hlfir::EntityWithAttributes> Fortran::lower::convertCallToHLFIR(
2835     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
2836     const evaluate::ProcedureRef &procRef, std::optional<mlir::Type> resultType,
2837     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
2838   auto &builder = converter.getFirOpBuilder();
2839   if (resultType && !procRef.IsElemental() &&
2840       isInWhereMaskedExpression(builder) &&
2841       !builder.getRegion().getParentOfType<hlfir::ExactlyOnceOp>()) {
2842     // Non elemental calls inside a where-assignment-stmt must be executed
2843     // exactly once without mask control. Lower them in a special region so that
2844     // this can be enforced whenscheduling forall/where expression evaluations.
2845     Fortran::lower::StatementContext localStmtCtx;
2846     mlir::Type bogusType = builder.getIndexType();
2847     auto exactlyOnce = builder.create<hlfir::ExactlyOnceOp>(loc, bogusType);
2848     mlir::Block *block = builder.createBlock(&exactlyOnce.getBody());
2849     builder.setInsertionPointToStart(block);
2850     CallContext callContext(procRef, resultType, loc, converter, symMap,
2851                             localStmtCtx);
2852     std::optional<hlfir::EntityWithAttributes> res =
2853         genProcedureRef(callContext);
2854     assert(res.has_value() && "must be a function");
2855     auto yield = builder.create<hlfir::YieldOp>(loc, *res);
2856     Fortran::lower::genCleanUpInRegionIfAny(loc, builder, yield.getCleanup(),
2857                                             localStmtCtx);
2858     builder.setInsertionPointAfter(exactlyOnce);
2859     exactlyOnce->getResult(0).setType(res->getType());
2860     if (hlfir::isFortranValue(exactlyOnce.getResult()))
2861       return hlfir::EntityWithAttributes{exactlyOnce.getResult()};
2862     // Create hlfir.declare for the result to satisfy
2863     // hlfir::EntityWithAttributes requirements.
2864     auto [exv, cleanup] = hlfir::translateToExtendedValue(
2865         loc, builder, hlfir::Entity{exactlyOnce});
2866     assert(!cleanup && "resut is a variable");
2867     return hlfir::genDeclare(loc, builder, exv, ".func.pointer.result",
2868                              fir::FortranVariableFlagsAttr{});
2869   }
2870   CallContext callContext(procRef, resultType, loc, converter, symMap, stmtCtx);
2871   return genProcedureRef(callContext);
2872 }
2873 
2874 void Fortran::lower::convertUserDefinedAssignmentToHLFIR(
2875     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
2876     const evaluate::ProcedureRef &procRef, hlfir::Entity lhs, hlfir::Entity rhs,
2877     Fortran::lower::SymMap &symMap) {
2878   Fortran::lower::StatementContext definedAssignmentContext;
2879   CallContext callContext(procRef, /*resultType=*/std::nullopt, loc, converter,
2880                           symMap, definedAssignmentContext);
2881   Fortran::lower::CallerInterface caller(procRef, converter);
2882   mlir::FunctionType callSiteType = caller.genFunctionType();
2883   PreparedActualArgument preparedLhs{lhs, /*isPresent=*/std::nullopt};
2884   PreparedActualArgument preparedRhs{rhs, /*isPresent=*/std::nullopt};
2885   PreparedActualArguments loweredActuals{preparedLhs, preparedRhs};
2886   genUserCall(loweredActuals, caller, callSiteType, callContext);
2887   return;
2888 }
2889