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

source code of flang/lib/Lower/ConvertCall.cpp