1 //===-- CallInterface.cpp -- Procedure call interface ---------------------===//
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 #include "flang/Lower/CallInterface.h"
10 #include "flang/Evaluate/fold.h"
11 #include "flang/Lower/Bridge.h"
12 #include "flang/Lower/Mangler.h"
13 #include "flang/Lower/PFTBuilder.h"
14 #include "flang/Lower/StatementContext.h"
15 #include "flang/Lower/Support/Utils.h"
16 #include "flang/Optimizer/Builder/Character.h"
17 #include "flang/Optimizer/Builder/FIRBuilder.h"
18 #include "flang/Optimizer/Builder/Todo.h"
19 #include "flang/Optimizer/Dialect/FIRDialect.h"
20 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
21 #include "flang/Optimizer/Support/InternalNames.h"
22 #include "flang/Semantics/symbol.h"
23 #include "flang/Semantics/tools.h"
24 
25 //===----------------------------------------------------------------------===//
26 // BIND(C) mangling helpers
27 //===----------------------------------------------------------------------===//
28 
29 // Return the binding label (from BIND(C...)) or the mangled name of a symbol.
getMangledName(mlir::Location loc,const Fortran::semantics::Symbol & symbol)30 static std::string getMangledName(mlir::Location loc,
31                                   const Fortran::semantics::Symbol &symbol) {
32   const std::string *bindName = symbol.GetBindName();
33   // TODO: update GetBindName so that it does not return a label for internal
34   // procedures.
35   if (bindName && Fortran::semantics::ClassifyProcedure(symbol) ==
36                       Fortran::semantics::ProcedureDefinitionClass::Internal)
37     TODO(loc, "BIND(C) internal procedures");
38   return bindName ? *bindName : Fortran::lower::mangle::mangleName(symbol);
39 }
40 
41 /// Return the type of a dummy procedure given its characteristic (if it has
42 /// one).
getProcedureDesignatorType(const Fortran::evaluate::characteristics::Procedure *,Fortran::lower::AbstractConverter & converter)43 mlir::Type getProcedureDesignatorType(
44     const Fortran::evaluate::characteristics::Procedure *,
45     Fortran::lower::AbstractConverter &converter) {
46   // TODO: Get actual function type of the dummy procedure, at least when an
47   // interface is given. The result type should be available even if the arity
48   // and type of the arguments is not.
49   llvm::SmallVector<mlir::Type> resultTys;
50   llvm::SmallVector<mlir::Type> inputTys;
51   // In general, that is a nice to have but we cannot guarantee to find the
52   // function type that will match the one of the calls, we may not even know
53   // how many arguments the dummy procedure accepts (e.g. if a procedure
54   // pointer is only transiting through the current procedure without being
55   // called), so a function type cast must always be inserted.
56   auto *context = &converter.getMLIRContext();
57   auto untypedFunc = mlir::FunctionType::get(context, inputTys, resultTys);
58   return fir::BoxProcType::get(context, untypedFunc);
59 }
60 
61 //===----------------------------------------------------------------------===//
62 // Caller side interface implementation
63 //===----------------------------------------------------------------------===//
64 
hasAlternateReturns() const65 bool Fortran::lower::CallerInterface::hasAlternateReturns() const {
66   return procRef.hasAlternateReturns();
67 }
68 
getMangledName() const69 std::string Fortran::lower::CallerInterface::getMangledName() const {
70   const Fortran::evaluate::ProcedureDesignator &proc = procRef.proc();
71   if (const Fortran::semantics::Symbol *symbol = proc.GetSymbol())
72     return ::getMangledName(converter.getCurrentLocation(),
73                             symbol->GetUltimate());
74   assert(proc.GetSpecificIntrinsic() &&
75          "expected intrinsic procedure in designator");
76   return proc.GetName();
77 }
78 
79 const Fortran::semantics::Symbol *
getProcedureSymbol() const80 Fortran::lower::CallerInterface::getProcedureSymbol() const {
81   return procRef.proc().GetSymbol();
82 }
83 
isIndirectCall() const84 bool Fortran::lower::CallerInterface::isIndirectCall() const {
85   if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
86     return Fortran::semantics::IsPointer(*symbol) ||
87            Fortran::semantics::IsDummy(*symbol);
88   return false;
89 }
90 
91 const Fortran::semantics::Symbol *
getIfIndirectCallSymbol() const92 Fortran::lower::CallerInterface::getIfIndirectCallSymbol() const {
93   if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
94     if (Fortran::semantics::IsPointer(*symbol) ||
95         Fortran::semantics::IsDummy(*symbol))
96       return symbol;
97   return nullptr;
98 }
99 
getCalleeLocation() const100 mlir::Location Fortran::lower::CallerInterface::getCalleeLocation() const {
101   const Fortran::evaluate::ProcedureDesignator &proc = procRef.proc();
102   // FIXME: If the callee is defined in the same file but after the current
103   // unit we cannot get its location here and the funcOp is created at the
104   // wrong location (i.e, the caller location).
105   if (const Fortran::semantics::Symbol *symbol = proc.GetSymbol())
106     return converter.genLocation(symbol->name());
107   // Use current location for intrinsics.
108   return converter.getCurrentLocation();
109 }
110 
111 // Get dummy argument characteristic for a procedure with implicit interface
112 // from the actual argument characteristic. The actual argument may not be a F77
113 // entity. The attribute must be dropped and the shape, if any, must be made
114 // explicit.
115 static Fortran::evaluate::characteristics::DummyDataObject
asImplicitArg(Fortran::evaluate::characteristics::DummyDataObject && dummy)116 asImplicitArg(Fortran::evaluate::characteristics::DummyDataObject &&dummy) {
117   Fortran::evaluate::Shape shape =
118       dummy.type.attrs().none() ? dummy.type.shape()
119                                 : Fortran::evaluate::Shape(dummy.type.Rank());
120   return Fortran::evaluate::characteristics::DummyDataObject(
121       Fortran::evaluate::characteristics::TypeAndShape(dummy.type.type(),
122                                                        std::move(shape)));
123 }
124 
125 static Fortran::evaluate::characteristics::DummyArgument
asImplicitArg(Fortran::evaluate::characteristics::DummyArgument && dummy)126 asImplicitArg(Fortran::evaluate::characteristics::DummyArgument &&dummy) {
127   return std::visit(
128       Fortran::common::visitors{
129           [&](Fortran::evaluate::characteristics::DummyDataObject &obj) {
130             return Fortran::evaluate::characteristics::DummyArgument(
131                 std::move(dummy.name), asImplicitArg(std::move(obj)));
132           },
133           [&](Fortran::evaluate::characteristics::DummyProcedure &proc) {
134             return Fortran::evaluate::characteristics::DummyArgument(
135                 std::move(dummy.name), std::move(proc));
136           },
137           [](Fortran::evaluate::characteristics::AlternateReturn &x) {
138             return Fortran::evaluate::characteristics::DummyArgument(
139                 std::move(x));
140           }},
141       dummy.u);
142 }
143 
144 Fortran::evaluate::characteristics::Procedure
characterize() const145 Fortran::lower::CallerInterface::characterize() const {
146   Fortran::evaluate::FoldingContext &foldingContext =
147       converter.getFoldingContext();
148   std::optional<Fortran::evaluate::characteristics::Procedure> characteristic =
149       Fortran::evaluate::characteristics::Procedure::Characterize(
150           procRef.proc(), foldingContext);
151   assert(characteristic && "Failed to get characteristic from procRef");
152   // The characteristic may not contain the argument characteristic if the
153   // ProcedureDesignator has no interface.
154   if (!characteristic->HasExplicitInterface()) {
155     for (const std::optional<Fortran::evaluate::ActualArgument> &arg :
156          procRef.arguments()) {
157       if (arg.value().isAlternateReturn()) {
158         characteristic->dummyArguments.emplace_back(
159             Fortran::evaluate::characteristics::AlternateReturn{});
160       } else {
161         // Argument cannot be optional with implicit interface
162         const Fortran::lower::SomeExpr *expr = arg.value().UnwrapExpr();
163         assert(
164             expr &&
165             "argument in call with implicit interface cannot be assumed type");
166         std::optional<Fortran::evaluate::characteristics::DummyArgument>
167             argCharacteristic =
168                 Fortran::evaluate::characteristics::DummyArgument::FromActual(
169                     "actual", *expr, foldingContext);
170         assert(argCharacteristic &&
171                "failed to characterize argument in implicit call");
172         characteristic->dummyArguments.emplace_back(
173             asImplicitArg(std::move(*argCharacteristic)));
174       }
175     }
176   }
177   return *characteristic;
178 }
179 
placeInput(const PassedEntity & passedEntity,mlir::Value arg)180 void Fortran::lower::CallerInterface::placeInput(
181     const PassedEntity &passedEntity, mlir::Value arg) {
182   assert(static_cast<int>(actualInputs.size()) > passedEntity.firArgument &&
183          passedEntity.firArgument >= 0 &&
184          passedEntity.passBy != CallInterface::PassEntityBy::AddressAndLength &&
185          "bad arg position");
186   actualInputs[passedEntity.firArgument] = arg;
187 }
188 
placeAddressAndLengthInput(const PassedEntity & passedEntity,mlir::Value addr,mlir::Value len)189 void Fortran::lower::CallerInterface::placeAddressAndLengthInput(
190     const PassedEntity &passedEntity, mlir::Value addr, mlir::Value len) {
191   assert(static_cast<int>(actualInputs.size()) > passedEntity.firArgument &&
192          static_cast<int>(actualInputs.size()) > passedEntity.firLength &&
193          passedEntity.firArgument >= 0 && passedEntity.firLength >= 0 &&
194          passedEntity.passBy == CallInterface::PassEntityBy::AddressAndLength &&
195          "bad arg position");
196   actualInputs[passedEntity.firArgument] = addr;
197   actualInputs[passedEntity.firLength] = len;
198 }
199 
verifyActualInputs() const200 bool Fortran::lower::CallerInterface::verifyActualInputs() const {
201   if (getNumFIRArguments() != actualInputs.size())
202     return false;
203   for (mlir::Value arg : actualInputs) {
204     if (!arg)
205       return false;
206   }
207   return true;
208 }
209 
walkResultLengths(ExprVisitor visitor) const210 void Fortran::lower::CallerInterface::walkResultLengths(
211     ExprVisitor visitor) const {
212   assert(characteristic && "characteristic was not computed");
213   const Fortran::evaluate::characteristics::FunctionResult &result =
214       characteristic->functionResult.value();
215   const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
216       result.GetTypeAndShape();
217   assert(typeAndShape && "no result type");
218   Fortran::evaluate::DynamicType dynamicType = typeAndShape->type();
219   // Visit result length specification expressions that are explicit.
220   if (dynamicType.category() == Fortran::common::TypeCategory::Character) {
221     if (std::optional<Fortran::evaluate::ExtentExpr> length =
222             dynamicType.GetCharLength())
223       visitor(toEvExpr(*length));
224   } else if (dynamicType.category() == common::TypeCategory::Derived) {
225     const Fortran::semantics::DerivedTypeSpec &derivedTypeSpec =
226         dynamicType.GetDerivedTypeSpec();
227     if (Fortran::semantics::CountLenParameters(derivedTypeSpec) > 0)
228       TODO(converter.getCurrentLocation(),
229            "function result with derived type length parameters");
230   }
231 }
232 
233 // Compute extent expr from shapeSpec of an explicit shape.
234 // TODO: Allow evaluate shape analysis to work in a mode where it disregards
235 // the non-constant aspects when building the shape to avoid having this here.
236 static Fortran::evaluate::ExtentExpr
getExtentExpr(const Fortran::semantics::ShapeSpec & shapeSpec)237 getExtentExpr(const Fortran::semantics::ShapeSpec &shapeSpec) {
238   const auto &ubound = shapeSpec.ubound().GetExplicit();
239   const auto &lbound = shapeSpec.lbound().GetExplicit();
240   assert(lbound && ubound && "shape must be explicit");
241   return Fortran::common::Clone(*ubound) - Fortran::common::Clone(*lbound) +
242          Fortran::evaluate::ExtentExpr{1};
243 }
244 
walkResultExtents(ExprVisitor visitor) const245 void Fortran::lower::CallerInterface::walkResultExtents(
246     ExprVisitor visitor) const {
247   // Walk directly the result symbol shape (the characteristic shape may contain
248   // descriptor inquiries to it that would fail to lower on the caller side).
249   const Fortran::semantics::SubprogramDetails *interfaceDetails =
250       getInterfaceDetails();
251   if (interfaceDetails) {
252     const Fortran::semantics::Symbol &result = interfaceDetails->result();
253     if (const auto *objectDetails =
254             result.detailsIf<Fortran::semantics::ObjectEntityDetails>())
255       if (objectDetails->shape().IsExplicitShape())
256         for (const Fortran::semantics::ShapeSpec &shapeSpec :
257              objectDetails->shape())
258           visitor(Fortran::evaluate::AsGenericExpr(getExtentExpr(shapeSpec)));
259   } else {
260     if (procRef.Rank() != 0)
261       fir::emitFatalError(
262           converter.getCurrentLocation(),
263           "only scalar functions may not have an interface symbol");
264   }
265 }
266 
mustMapInterfaceSymbols() const267 bool Fortran::lower::CallerInterface::mustMapInterfaceSymbols() const {
268   assert(characteristic && "characteristic was not computed");
269   const std::optional<Fortran::evaluate::characteristics::FunctionResult>
270       &result = characteristic->functionResult;
271   if (!result || result->CanBeReturnedViaImplicitInterface() ||
272       !getInterfaceDetails())
273     return false;
274   bool allResultSpecExprConstant = true;
275   auto visitor = [&](const Fortran::lower::SomeExpr &e) {
276     allResultSpecExprConstant &= Fortran::evaluate::IsConstantExpr(e);
277   };
278   walkResultLengths(visitor);
279   walkResultExtents(visitor);
280   return !allResultSpecExprConstant;
281 }
282 
getArgumentValue(const semantics::Symbol & sym) const283 mlir::Value Fortran::lower::CallerInterface::getArgumentValue(
284     const semantics::Symbol &sym) const {
285   mlir::Location loc = converter.getCurrentLocation();
286   const Fortran::semantics::SubprogramDetails *ifaceDetails =
287       getInterfaceDetails();
288   if (!ifaceDetails)
289     fir::emitFatalError(
290         loc, "mapping actual and dummy arguments requires an interface");
291   const std::vector<Fortran::semantics::Symbol *> &dummies =
292       ifaceDetails->dummyArgs();
293   auto it = std::find(dummies.begin(), dummies.end(), &sym);
294   if (it == dummies.end())
295     fir::emitFatalError(loc, "symbol is not a dummy in this call");
296   FirValue mlirArgIndex = passedArguments[it - dummies.begin()].firArgument;
297   return actualInputs[mlirArgIndex];
298 }
299 
getResultStorageType() const300 mlir::Type Fortran::lower::CallerInterface::getResultStorageType() const {
301   if (passedResult)
302     return fir::dyn_cast_ptrEleTy(inputs[passedResult->firArgument].type);
303   assert(saveResult && !outputs.empty());
304   return outputs[0].type;
305 }
306 
307 const Fortran::semantics::Symbol &
getResultSymbol() const308 Fortran::lower::CallerInterface::getResultSymbol() const {
309   mlir::Location loc = converter.getCurrentLocation();
310   const Fortran::semantics::SubprogramDetails *ifaceDetails =
311       getInterfaceDetails();
312   if (!ifaceDetails)
313     fir::emitFatalError(
314         loc, "mapping actual and dummy arguments requires an interface");
315   return ifaceDetails->result();
316 }
317 
318 const Fortran::semantics::SubprogramDetails *
getInterfaceDetails() const319 Fortran::lower::CallerInterface::getInterfaceDetails() const {
320   if (const Fortran::semantics::Symbol *iface =
321           procRef.proc().GetInterfaceSymbol())
322     return iface->GetUltimate()
323         .detailsIf<Fortran::semantics::SubprogramDetails>();
324   return nullptr;
325 }
326 
327 //===----------------------------------------------------------------------===//
328 // Callee side interface implementation
329 //===----------------------------------------------------------------------===//
330 
hasAlternateReturns() const331 bool Fortran::lower::CalleeInterface::hasAlternateReturns() const {
332   return !funit.isMainProgram() &&
333          Fortran::semantics::HasAlternateReturns(funit.getSubprogramSymbol());
334 }
335 
getMangledName() const336 std::string Fortran::lower::CalleeInterface::getMangledName() const {
337   if (funit.isMainProgram())
338     return fir::NameUniquer::doProgramEntry().str();
339   return ::getMangledName(converter.getCurrentLocation(),
340                           funit.getSubprogramSymbol());
341 }
342 
343 const Fortran::semantics::Symbol *
getProcedureSymbol() const344 Fortran::lower::CalleeInterface::getProcedureSymbol() const {
345   if (funit.isMainProgram())
346     return nullptr;
347   return &funit.getSubprogramSymbol();
348 }
349 
getCalleeLocation() const350 mlir::Location Fortran::lower::CalleeInterface::getCalleeLocation() const {
351   // FIXME: do NOT use unknown for the anonymous PROGRAM case. We probably
352   // should just stash the location in the funit regardless.
353   return converter.genLocation(funit.getStartingSourceLoc());
354 }
355 
356 Fortran::evaluate::characteristics::Procedure
characterize() const357 Fortran::lower::CalleeInterface::characterize() const {
358   Fortran::evaluate::FoldingContext &foldingContext =
359       converter.getFoldingContext();
360   std::optional<Fortran::evaluate::characteristics::Procedure> characteristic =
361       Fortran::evaluate::characteristics::Procedure::Characterize(
362           funit.getSubprogramSymbol(), foldingContext);
363   assert(characteristic && "Fail to get characteristic from symbol");
364   return *characteristic;
365 }
366 
isMainProgram() const367 bool Fortran::lower::CalleeInterface::isMainProgram() const {
368   return funit.isMainProgram();
369 }
370 
371 mlir::func::FuncOp
addEntryBlockAndMapArguments()372 Fortran::lower::CalleeInterface::addEntryBlockAndMapArguments() {
373   // Check for bugs in the front end. The front end must not present multiple
374   // definitions of the same procedure.
375   if (!func.getBlocks().empty())
376     fir::emitFatalError(func.getLoc(),
377                         "cannot process subprogram that was already processed");
378 
379   // On the callee side, directly map the mlir::value argument of the function
380   // block to the Fortran symbols.
381   func.addEntryBlock();
382   mapPassedEntities();
383   return func;
384 }
385 
hasHostAssociated() const386 bool Fortran::lower::CalleeInterface::hasHostAssociated() const {
387   return funit.parentHasHostAssoc();
388 }
389 
getHostAssociatedTy() const390 mlir::Type Fortran::lower::CalleeInterface::getHostAssociatedTy() const {
391   assert(hasHostAssociated());
392   return funit.parentHostAssoc().getArgumentType(converter);
393 }
394 
getHostAssociatedTuple() const395 mlir::Value Fortran::lower::CalleeInterface::getHostAssociatedTuple() const {
396   assert(hasHostAssociated() || !funit.getHostAssoc().empty());
397   return converter.hostAssocTupleValue();
398 }
399 
400 //===----------------------------------------------------------------------===//
401 // CallInterface implementation: this part is common to both caller and caller
402 // sides.
403 //===----------------------------------------------------------------------===//
404 
addSymbolAttribute(mlir::func::FuncOp func,const Fortran::semantics::Symbol & sym,mlir::MLIRContext & mlirContext)405 static void addSymbolAttribute(mlir::func::FuncOp func,
406                                const Fortran::semantics::Symbol &sym,
407                                mlir::MLIRContext &mlirContext) {
408   // Only add this on bind(C) functions for which the symbol is not reflected in
409   // the current context.
410   if (!Fortran::semantics::IsBindCProcedure(sym))
411     return;
412   std::string name =
413       Fortran::lower::mangle::mangleName(sym, /*keepExternalInScope=*/true);
414   func->setAttr(fir::getSymbolAttrName(),
415                 mlir::StringAttr::get(&mlirContext, name));
416 }
417 
418 /// Declare drives the different actions to be performed while analyzing the
419 /// signature and building/finding the mlir::func::FuncOp.
420 template <typename T>
declare()421 void Fortran::lower::CallInterface<T>::declare() {
422   if (!side().isMainProgram()) {
423     characteristic.emplace(side().characterize());
424     bool isImplicit = characteristic->CanBeCalledViaImplicitInterface();
425     determineInterface(isImplicit, *characteristic);
426   }
427   // No input/output for main program
428 
429   // Create / get funcOp for direct calls. For indirect calls (only meaningful
430   // on the caller side), no funcOp has to be created here. The mlir::Value
431   // holding the indirection is used when creating the fir::CallOp.
432   if (!side().isIndirectCall()) {
433     std::string name = side().getMangledName();
434     mlir::ModuleOp module = converter.getModuleOp();
435     func = fir::FirOpBuilder::getNamedFunction(module, name);
436     if (!func) {
437       mlir::Location loc = side().getCalleeLocation();
438       mlir::FunctionType ty = genFunctionType();
439       func = fir::FirOpBuilder::createFunction(loc, module, name, ty);
440       if (const Fortran::semantics::Symbol *sym = side().getProcedureSymbol())
441         addSymbolAttribute(func, *sym, converter.getMLIRContext());
442       for (const auto &placeHolder : llvm::enumerate(inputs))
443         if (!placeHolder.value().attributes.empty())
444           func.setArgAttrs(placeHolder.index(), placeHolder.value().attributes);
445     }
446   }
447 }
448 
449 /// Once the signature has been analyzed and the mlir::func::FuncOp was
450 /// built/found, map the fir inputs to Fortran entities (the symbols or
451 /// expressions).
452 template <typename T>
mapPassedEntities()453 void Fortran::lower::CallInterface<T>::mapPassedEntities() {
454   // map back fir inputs to passed entities
455   if constexpr (std::is_same_v<T, Fortran::lower::CalleeInterface>) {
456     assert(inputs.size() == func.front().getArguments().size() &&
457            "function previously created with different number of arguments");
458     for (auto [fst, snd] : llvm::zip(inputs, func.front().getArguments()))
459       mapBackInputToPassedEntity(fst, snd);
460   } else {
461     // On the caller side, map the index of the mlir argument position
462     // to Fortran ActualArguments.
463     int firPosition = 0;
464     for (const FirPlaceHolder &placeHolder : inputs)
465       mapBackInputToPassedEntity(placeHolder, firPosition++);
466   }
467 }
468 
469 template <typename T>
mapBackInputToPassedEntity(const FirPlaceHolder & placeHolder,FirValue firValue)470 void Fortran::lower::CallInterface<T>::mapBackInputToPassedEntity(
471     const FirPlaceHolder &placeHolder, FirValue firValue) {
472   PassedEntity &passedEntity =
473       placeHolder.passedEntityPosition == FirPlaceHolder::resultEntityPosition
474           ? passedResult.value()
475           : passedArguments[placeHolder.passedEntityPosition];
476   if (placeHolder.property == Property::CharLength)
477     passedEntity.firLength = firValue;
478   else
479     passedEntity.firArgument = firValue;
480 }
481 
482 /// Helpers to access ActualArgument/Symbols
483 static const Fortran::evaluate::ActualArguments &
getEntityContainer(const Fortran::evaluate::ProcedureRef & proc)484 getEntityContainer(const Fortran::evaluate::ProcedureRef &proc) {
485   return proc.arguments();
486 }
487 
488 static const std::vector<Fortran::semantics::Symbol *> &
getEntityContainer(Fortran::lower::pft::FunctionLikeUnit & funit)489 getEntityContainer(Fortran::lower::pft::FunctionLikeUnit &funit) {
490   return funit.getSubprogramSymbol()
491       .get<Fortran::semantics::SubprogramDetails>()
492       .dummyArgs();
493 }
494 
getDataObjectEntity(const std::optional<Fortran::evaluate::ActualArgument> & arg)495 static const Fortran::evaluate::ActualArgument *getDataObjectEntity(
496     const std::optional<Fortran::evaluate::ActualArgument> &arg) {
497   if (arg)
498     return &*arg;
499   return nullptr;
500 }
501 
502 static const Fortran::semantics::Symbol &
getDataObjectEntity(const Fortran::semantics::Symbol * arg)503 getDataObjectEntity(const Fortran::semantics::Symbol *arg) {
504   assert(arg && "expect symbol for data object entity");
505   return *arg;
506 }
507 
508 static const Fortran::evaluate::ActualArgument *
getResultEntity(const Fortran::evaluate::ProcedureRef &)509 getResultEntity(const Fortran::evaluate::ProcedureRef &) {
510   return nullptr;
511 }
512 
513 static const Fortran::semantics::Symbol &
getResultEntity(Fortran::lower::pft::FunctionLikeUnit & funit)514 getResultEntity(Fortran::lower::pft::FunctionLikeUnit &funit) {
515   return funit.getSubprogramSymbol()
516       .get<Fortran::semantics::SubprogramDetails>()
517       .result();
518 }
519 
520 /// Bypass helpers to manipulate entities since they are not any symbol/actual
521 /// argument to associate. See SignatureBuilder below.
522 using FakeEntity = bool;
523 using FakeEntities = llvm::SmallVector<FakeEntity>;
524 static FakeEntities
getEntityContainer(const Fortran::evaluate::characteristics::Procedure & proc)525 getEntityContainer(const Fortran::evaluate::characteristics::Procedure &proc) {
526   FakeEntities enities(proc.dummyArguments.size());
527   return enities;
528 }
getDataObjectEntity(const FakeEntity & e)529 static const FakeEntity &getDataObjectEntity(const FakeEntity &e) { return e; }
530 static FakeEntity
getResultEntity(const Fortran::evaluate::characteristics::Procedure & proc)531 getResultEntity(const Fortran::evaluate::characteristics::Procedure &proc) {
532   return false;
533 }
534 
535 /// This is the actual part that defines the FIR interface based on the
536 /// characteristic. It directly mutates the CallInterface members.
537 template <typename T>
538 class Fortran::lower::CallInterfaceImpl {
539   using CallInterface = Fortran::lower::CallInterface<T>;
540   using PassEntityBy = typename CallInterface::PassEntityBy;
541   using PassedEntity = typename CallInterface::PassedEntity;
542   using FirValue = typename CallInterface::FirValue;
543   using FortranEntity = typename CallInterface::FortranEntity;
544   using FirPlaceHolder = typename CallInterface::FirPlaceHolder;
545   using Property = typename CallInterface::Property;
546   using TypeAndShape = Fortran::evaluate::characteristics::TypeAndShape;
547   using DummyCharacteristics =
548       Fortran::evaluate::characteristics::DummyArgument;
549 
550 public:
CallInterfaceImpl(CallInterface & i)551   CallInterfaceImpl(CallInterface &i)
552       : interface(i), mlirContext{i.converter.getMLIRContext()} {}
553 
buildImplicitInterface(const Fortran::evaluate::characteristics::Procedure & procedure)554   void buildImplicitInterface(
555       const Fortran::evaluate::characteristics::Procedure &procedure) {
556     // Handle result
557     if (const std::optional<Fortran::evaluate::characteristics::FunctionResult>
558             &result = procedure.functionResult)
559       handleImplicitResult(*result);
560     else if (interface.side().hasAlternateReturns())
561       addFirResult(mlir::IndexType::get(&mlirContext),
562                    FirPlaceHolder::resultEntityPosition, Property::Value);
563     // Handle arguments
564     const auto &argumentEntities =
565         getEntityContainer(interface.side().getCallDescription());
566     for (auto pair : llvm::zip(procedure.dummyArguments, argumentEntities)) {
567       const Fortran::evaluate::characteristics::DummyArgument
568           &argCharacteristics = std::get<0>(pair);
569       std::visit(
570           Fortran::common::visitors{
571               [&](const auto &dummy) {
572                 const auto &entity = getDataObjectEntity(std::get<1>(pair));
573                 handleImplicitDummy(&argCharacteristics, dummy, entity);
574               },
575               [&](const Fortran::evaluate::characteristics::AlternateReturn &) {
576                 // nothing to do
577               },
578           },
579           argCharacteristics.u);
580     }
581   }
582 
buildExplicitInterface(const Fortran::evaluate::characteristics::Procedure & procedure)583   void buildExplicitInterface(
584       const Fortran::evaluate::characteristics::Procedure &procedure) {
585     // Handle result
586     if (const std::optional<Fortran::evaluate::characteristics::FunctionResult>
587             &result = procedure.functionResult) {
588       if (result->CanBeReturnedViaImplicitInterface())
589         handleImplicitResult(*result);
590       else
591         handleExplicitResult(*result);
592     } else if (interface.side().hasAlternateReturns()) {
593       addFirResult(mlir::IndexType::get(&mlirContext),
594                    FirPlaceHolder::resultEntityPosition, Property::Value);
595     }
596     bool isBindC = procedure.IsBindC();
597     // Handle arguments
598     const auto &argumentEntities =
599         getEntityContainer(interface.side().getCallDescription());
600     for (auto pair : llvm::zip(procedure.dummyArguments, argumentEntities)) {
601       const Fortran::evaluate::characteristics::DummyArgument
602           &argCharacteristics = std::get<0>(pair);
603       std::visit(
604           Fortran::common::visitors{
605               [&](const Fortran::evaluate::characteristics::DummyDataObject
606                       &dummy) {
607                 const auto &entity = getDataObjectEntity(std::get<1>(pair));
608                 if (dummy.CanBePassedViaImplicitInterface())
609                   handleImplicitDummy(&argCharacteristics, dummy, entity);
610                 else
611                   handleExplicitDummy(&argCharacteristics, dummy, entity,
612                                       isBindC);
613               },
614               [&](const Fortran::evaluate::characteristics::DummyProcedure
615                       &dummy) {
616                 const auto &entity = getDataObjectEntity(std::get<1>(pair));
617                 handleImplicitDummy(&argCharacteristics, dummy, entity);
618               },
619               [&](const Fortran::evaluate::characteristics::AlternateReturn &) {
620                 // nothing to do
621               },
622           },
623           argCharacteristics.u);
624     }
625   }
626 
appendHostAssocTupleArg(mlir::Type tupTy)627   void appendHostAssocTupleArg(mlir::Type tupTy) {
628     mlir::MLIRContext *ctxt = tupTy.getContext();
629     addFirOperand(tupTy, nextPassedArgPosition(), Property::BaseAddress,
630                   {mlir::NamedAttribute{
631                       mlir::StringAttr::get(ctxt, fir::getHostAssocAttrName()),
632                       mlir::UnitAttr::get(ctxt)}});
633     interface.passedArguments.emplace_back(
634         PassedEntity{PassEntityBy::BaseAddress, std::nullopt,
635                      interface.side().getHostAssociatedTuple(), emptyValue()});
636   }
637 
getResultDynamicType(const Fortran::evaluate::characteristics::Procedure & procedure)638   static llvm::Optional<Fortran::evaluate::DynamicType> getResultDynamicType(
639       const Fortran::evaluate::characteristics::Procedure &procedure) {
640     if (const std::optional<Fortran::evaluate::characteristics::FunctionResult>
641             &result = procedure.functionResult)
642       if (const auto *resultTypeAndShape = result->GetTypeAndShape())
643         return resultTypeAndShape->type();
644     return llvm::None;
645   }
646 
mustPassLengthWithDummyProcedure(const Fortran::evaluate::characteristics::Procedure & procedure)647   static bool mustPassLengthWithDummyProcedure(
648       const Fortran::evaluate::characteristics::Procedure &procedure) {
649     // When passing a character function designator `bar` as dummy procedure to
650     // `foo` (e.g. `foo(bar)`), pass the result length of `bar` to `foo` so that
651     // `bar` can be called inside `foo` even if its length is assumed there.
652     // From an ABI perspective, the extra length argument must be handled
653     // exactly as if passing a character object. Using an argument of
654     // fir.boxchar type gives the expected behavior: after codegen, the
655     // fir.boxchar lengths are added after all the arguments as extra value
656     // arguments (the extra arguments order is the order of the fir.boxchar).
657 
658     // This ABI is compatible with ifort, nag, nvfortran, and xlf, but not
659     // gfortran. Gfortran does not pass the length and is therefore unable to
660     // handle later call to `bar` in `foo` where the length would be assumed. If
661     // the result is an array, nag and ifort and xlf still pass the length, but
662     // not nvfortran (and gfortran). It is not clear it is possible to call an
663     // array function with assumed length (f18 forbides defining such
664     // interfaces). Hence, passing the length is most likely useless, but stick
665     // with ifort/nag/xlf interface here.
666     if (llvm::Optional<Fortran::evaluate::DynamicType> type =
667             getResultDynamicType(procedure))
668       return type->category() == Fortran::common::TypeCategory::Character;
669     return false;
670   }
671 
672 private:
handleImplicitResult(const Fortran::evaluate::characteristics::FunctionResult & result)673   void handleImplicitResult(
674       const Fortran::evaluate::characteristics::FunctionResult &result) {
675     if (result.IsProcedurePointer())
676       TODO(interface.converter.getCurrentLocation(),
677            "procedure pointer result not yet handled");
678     const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
679         result.GetTypeAndShape();
680     assert(typeAndShape && "expect type for non proc pointer result");
681     Fortran::evaluate::DynamicType dynamicType = typeAndShape->type();
682     // Character result allocated by caller and passed as hidden arguments
683     if (dynamicType.category() == Fortran::common::TypeCategory::Character) {
684       handleImplicitCharacterResult(dynamicType);
685     } else if (dynamicType.category() ==
686                Fortran::common::TypeCategory::Derived) {
687       // Derived result need to be allocated by the caller and the result value
688       // must be saved. Derived type in implicit interface cannot have length
689       // parameters.
690       setSaveResult();
691       mlir::Type mlirType = translateDynamicType(dynamicType);
692       addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
693                    Property::Value);
694     } else {
695       // All result other than characters/derived are simply returned by value
696       // in implicit interfaces
697       mlir::Type mlirType =
698           getConverter().genType(dynamicType.category(), dynamicType.kind());
699       addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
700                    Property::Value);
701     }
702   }
703   void
handleImplicitCharacterResult(const Fortran::evaluate::DynamicType & type)704   handleImplicitCharacterResult(const Fortran::evaluate::DynamicType &type) {
705     int resultPosition = FirPlaceHolder::resultEntityPosition;
706     setPassedResult(PassEntityBy::AddressAndLength,
707                     getResultEntity(interface.side().getCallDescription()));
708     mlir::Type lenTy = mlir::IndexType::get(&mlirContext);
709     std::optional<std::int64_t> constantLen = type.knownLength();
710     fir::CharacterType::LenType len =
711         constantLen ? *constantLen : fir::CharacterType::unknownLen();
712     mlir::Type charRefTy = fir::ReferenceType::get(
713         fir::CharacterType::get(&mlirContext, type.kind(), len));
714     mlir::Type boxCharTy = fir::BoxCharType::get(&mlirContext, type.kind());
715     addFirOperand(charRefTy, resultPosition, Property::CharAddress);
716     addFirOperand(lenTy, resultPosition, Property::CharLength);
717     /// For now, also return it by boxchar
718     addFirResult(boxCharTy, resultPosition, Property::BoxChar);
719   }
720 
721   /// Return a vector with an attribute with the name of the argument if this
722   /// is a callee interface and the name is available. Otherwise, just return
723   /// an empty vector.
724   llvm::SmallVector<mlir::NamedAttribute>
dummyNameAttr(const FortranEntity & entity)725   dummyNameAttr(const FortranEntity &entity) {
726     if constexpr (std::is_same_v<FortranEntity,
727                                  std::optional<Fortran::common::Reference<
728                                      const Fortran::semantics::Symbol>>>) {
729       if (entity.has_value()) {
730         const Fortran::semantics::Symbol *argument = &*entity.value();
731         // "fir.bindc_name" is used for arguments for the sake of consistency
732         // with other attributes carrying surface syntax names in FIR.
733         return {mlir::NamedAttribute(
734             mlir::StringAttr::get(&mlirContext, "fir.bindc_name"),
735             mlir::StringAttr::get(&mlirContext,
736                                   toStringRef(argument->name())))};
737       }
738     }
739     return {};
740   }
741 
handleImplicitDummy(const DummyCharacteristics * characteristics,const Fortran::evaluate::characteristics::DummyDataObject & obj,const FortranEntity & entity)742   void handleImplicitDummy(
743       const DummyCharacteristics *characteristics,
744       const Fortran::evaluate::characteristics::DummyDataObject &obj,
745       const FortranEntity &entity) {
746     Fortran::evaluate::DynamicType dynamicType = obj.type.type();
747     if (dynamicType.category() == Fortran::common::TypeCategory::Character) {
748       mlir::Type boxCharTy =
749           fir::BoxCharType::get(&mlirContext, dynamicType.kind());
750       addFirOperand(boxCharTy, nextPassedArgPosition(), Property::BoxChar,
751                     dummyNameAttr(entity));
752       addPassedArg(PassEntityBy::BoxChar, entity, characteristics);
753     } else {
754       // non-PDT derived type allowed in implicit interface.
755       mlir::Type type = translateDynamicType(dynamicType);
756       fir::SequenceType::Shape bounds = getBounds(obj.type.shape());
757       if (!bounds.empty())
758         type = fir::SequenceType::get(bounds, type);
759       mlir::Type refType = fir::ReferenceType::get(type);
760       addFirOperand(refType, nextPassedArgPosition(), Property::BaseAddress,
761                     dummyNameAttr(entity));
762       addPassedArg(PassEntityBy::BaseAddress, entity, characteristics);
763     }
764   }
765 
766   // Define when an explicit argument must be passed in a fir.box.
dummyRequiresBox(const Fortran::evaluate::characteristics::DummyDataObject & obj)767   bool dummyRequiresBox(
768       const Fortran::evaluate::characteristics::DummyDataObject &obj) {
769     using ShapeAttr = Fortran::evaluate::characteristics::TypeAndShape::Attr;
770     using ShapeAttrs = Fortran::evaluate::characteristics::TypeAndShape::Attrs;
771     constexpr ShapeAttrs shapeRequiringBox = {
772         ShapeAttr::AssumedShape, ShapeAttr::DeferredShape,
773         ShapeAttr::AssumedRank, ShapeAttr::Coarray};
774     if ((obj.type.attrs() & shapeRequiringBox).any())
775       // Need to pass shape/coshape info in fir.box.
776       return true;
777     if (obj.type.type().IsPolymorphic())
778       // Need to pass dynamic type info in fir.box.
779       return true;
780     if (const Fortran::semantics::DerivedTypeSpec *derived =
781             Fortran::evaluate::GetDerivedTypeSpec(obj.type.type()))
782       if (const Fortran::semantics::Scope *scope = derived->scope())
783         // Need to pass length type parameters in fir.box if any.
784         return scope->IsDerivedTypeWithLengthParameter();
785     return false;
786   }
787 
788   mlir::Type
translateDynamicType(const Fortran::evaluate::DynamicType & dynamicType)789   translateDynamicType(const Fortran::evaluate::DynamicType &dynamicType) {
790     Fortran::common::TypeCategory cat = dynamicType.category();
791     // DERIVED
792     if (cat == Fortran::common::TypeCategory::Derived) {
793       if (dynamicType.IsPolymorphic())
794         TODO(interface.converter.getCurrentLocation(),
795              "support for polymorphic types");
796       return getConverter().genType(dynamicType.GetDerivedTypeSpec());
797     }
798     // CHARACTER with compile time constant length.
799     if (cat == Fortran::common::TypeCategory::Character)
800       if (std::optional<std::int64_t> constantLen =
801               toInt64(dynamicType.GetCharLength()))
802         return getConverter().genType(cat, dynamicType.kind(), {*constantLen});
803     // INTEGER, REAL, LOGICAL, COMPLEX, and CHARACTER with dynamic length.
804     return getConverter().genType(cat, dynamicType.kind());
805   }
806 
handleExplicitDummy(const DummyCharacteristics * characteristics,const Fortran::evaluate::characteristics::DummyDataObject & obj,const FortranEntity & entity,bool isBindC)807   void handleExplicitDummy(
808       const DummyCharacteristics *characteristics,
809       const Fortran::evaluate::characteristics::DummyDataObject &obj,
810       const FortranEntity &entity, bool isBindC) {
811     using Attrs = Fortran::evaluate::characteristics::DummyDataObject::Attr;
812 
813     bool isValueAttr = false;
814     [[maybe_unused]] mlir::Location loc =
815         interface.converter.getCurrentLocation();
816     llvm::SmallVector<mlir::NamedAttribute> attrs = dummyNameAttr(entity);
817     auto addMLIRAttr = [&](llvm::StringRef attr) {
818       attrs.emplace_back(mlir::StringAttr::get(&mlirContext, attr),
819                          mlir::UnitAttr::get(&mlirContext));
820     };
821     if (obj.attrs.test(Attrs::Optional))
822       addMLIRAttr(fir::getOptionalAttrName());
823     if (obj.attrs.test(Attrs::Asynchronous))
824       TODO(loc, "ASYNCHRONOUS in procedure interface");
825     if (obj.attrs.test(Attrs::Contiguous))
826       addMLIRAttr(fir::getContiguousAttrName());
827     if (obj.attrs.test(Attrs::Value))
828       isValueAttr = true; // TODO: do we want an mlir::Attribute as well?
829     if (obj.attrs.test(Attrs::Volatile))
830       TODO(loc, "VOLATILE in procedure interface");
831     if (obj.attrs.test(Attrs::Target))
832       addMLIRAttr(fir::getTargetAttrName());
833 
834     // TODO: intents that require special care (e.g finalization)
835 
836     using ShapeAttr = Fortran::evaluate::characteristics::TypeAndShape::Attr;
837     const Fortran::evaluate::characteristics::TypeAndShape::Attrs &shapeAttrs =
838         obj.type.attrs();
839     if (shapeAttrs.test(ShapeAttr::AssumedRank))
840       TODO(loc, "assumed rank in procedure interface");
841     if (shapeAttrs.test(ShapeAttr::Coarray))
842       TODO(loc, "coarray in procedure interface");
843 
844     // So far assume that if the argument cannot be passed by implicit interface
845     // it must be by box. That may no be always true (e.g for simple optionals)
846 
847     Fortran::evaluate::DynamicType dynamicType = obj.type.type();
848     mlir::Type type = translateDynamicType(dynamicType);
849     fir::SequenceType::Shape bounds = getBounds(obj.type.shape());
850     if (!bounds.empty())
851       type = fir::SequenceType::get(bounds, type);
852     if (obj.attrs.test(Attrs::Allocatable))
853       type = fir::HeapType::get(type);
854     if (obj.attrs.test(Attrs::Pointer))
855       type = fir::PointerType::get(type);
856     mlir::Type boxType = fir::BoxType::get(type);
857 
858     if (obj.attrs.test(Attrs::Allocatable) || obj.attrs.test(Attrs::Pointer)) {
859       // Pass as fir.ref<fir.box>
860       mlir::Type boxRefType = fir::ReferenceType::get(boxType);
861       addFirOperand(boxRefType, nextPassedArgPosition(), Property::MutableBox,
862                     attrs);
863       addPassedArg(PassEntityBy::MutableBox, entity, characteristics);
864     } else if (dummyRequiresBox(obj)) {
865       // Pass as fir.box
866       if (isValueAttr)
867         TODO(loc, "assumed shape dummy argument with VALUE attribute");
868       addFirOperand(boxType, nextPassedArgPosition(), Property::Box, attrs);
869       addPassedArg(PassEntityBy::Box, entity, characteristics);
870     } else if (dynamicType.category() ==
871                Fortran::common::TypeCategory::Character) {
872       // Pass as fir.box_char
873       mlir::Type boxCharTy =
874           fir::BoxCharType::get(&mlirContext, dynamicType.kind());
875       addFirOperand(boxCharTy, nextPassedArgPosition(), Property::BoxChar,
876                     attrs);
877       addPassedArg(isValueAttr ? PassEntityBy::CharBoxValueAttribute
878                                : PassEntityBy::BoxChar,
879                    entity, characteristics);
880     } else {
881       // Pass as fir.ref unless it's by VALUE and BIND(C)
882       mlir::Type passType = fir::ReferenceType::get(type);
883       PassEntityBy passBy = PassEntityBy::BaseAddress;
884       Property prop = Property::BaseAddress;
885       if (isValueAttr) {
886         if (isBindC) {
887           passBy = PassEntityBy::Value;
888           prop = Property::Value;
889           passType = type;
890         } else {
891           passBy = PassEntityBy::BaseAddressValueAttribute;
892         }
893       }
894       addFirOperand(passType, nextPassedArgPosition(), prop, attrs);
895       addPassedArg(passBy, entity, characteristics);
896     }
897   }
898 
handleImplicitDummy(const DummyCharacteristics * characteristics,const Fortran::evaluate::characteristics::DummyProcedure & proc,const FortranEntity & entity)899   void handleImplicitDummy(
900       const DummyCharacteristics *characteristics,
901       const Fortran::evaluate::characteristics::DummyProcedure &proc,
902       const FortranEntity &entity) {
903     if (proc.attrs.test(
904             Fortran::evaluate::characteristics::DummyProcedure::Attr::Pointer))
905       TODO(interface.converter.getCurrentLocation(),
906            "procedure pointer arguments");
907     // Otherwise, it is a dummy procedure.
908     const Fortran::evaluate::characteristics::Procedure &procedure =
909         proc.procedure.value();
910     mlir::Type funcType =
911         getProcedureDesignatorType(&procedure, interface.converter);
912     llvm::Optional<Fortran::evaluate::DynamicType> resultTy =
913         getResultDynamicType(procedure);
914     if (resultTy && mustPassLengthWithDummyProcedure(procedure)) {
915       // The result length of dummy procedures that are character functions must
916       // be passed so that the dummy procedure can be called if it has assumed
917       // length on the callee side.
918       mlir::Type tupleType =
919           fir::factory::getCharacterProcedureTupleType(funcType);
920       llvm::StringRef charProcAttr = fir::getCharacterProcedureDummyAttrName();
921       addFirOperand(tupleType, nextPassedArgPosition(), Property::CharProcTuple,
922                     {mlir::NamedAttribute{
923                         mlir::StringAttr::get(&mlirContext, charProcAttr),
924                         mlir::UnitAttr::get(&mlirContext)}});
925       addPassedArg(PassEntityBy::CharProcTuple, entity, characteristics);
926       return;
927     }
928     addFirOperand(funcType, nextPassedArgPosition(), Property::BaseAddress);
929     addPassedArg(PassEntityBy::BaseAddress, entity, characteristics);
930   }
931 
handleExplicitResult(const Fortran::evaluate::characteristics::FunctionResult & result)932   void handleExplicitResult(
933       const Fortran::evaluate::characteristics::FunctionResult &result) {
934     using Attr = Fortran::evaluate::characteristics::FunctionResult::Attr;
935 
936     if (result.IsProcedurePointer())
937       TODO(interface.converter.getCurrentLocation(),
938            "procedure pointer results");
939     const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
940         result.GetTypeAndShape();
941     assert(typeAndShape && "expect type for non proc pointer result");
942     mlir::Type mlirType = translateDynamicType(typeAndShape->type());
943     fir::SequenceType::Shape bounds = getBounds(typeAndShape->shape());
944     if (!bounds.empty())
945       mlirType = fir::SequenceType::get(bounds, mlirType);
946     if (result.attrs.test(Attr::Allocatable))
947       mlirType = fir::BoxType::get(fir::HeapType::get(mlirType));
948     if (result.attrs.test(Attr::Pointer))
949       mlirType = fir::BoxType::get(fir::PointerType::get(mlirType));
950 
951     if (fir::isa_char(mlirType)) {
952       // Character scalar results must be passed as arguments in lowering so
953       // that an assumed length character function callee can access the result
954       // length. A function with a result requiring an explicit interface does
955       // not have to be compatible with assumed length function, but most
956       // compilers supports it.
957       handleImplicitCharacterResult(typeAndShape->type());
958       return;
959     }
960 
961     addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
962                  Property::Value);
963     // Explicit results require the caller to allocate the storage and save the
964     // function result in the storage with a fir.save_result.
965     setSaveResult();
966   }
967 
getBounds(const Fortran::evaluate::Shape & shape)968   fir::SequenceType::Shape getBounds(const Fortran::evaluate::Shape &shape) {
969     fir::SequenceType::Shape bounds;
970     for (const std::optional<Fortran::evaluate::ExtentExpr> &extent : shape) {
971       fir::SequenceType::Extent bound = fir::SequenceType::getUnknownExtent();
972       if (std::optional<std::int64_t> i = toInt64(extent))
973         bound = *i;
974       bounds.emplace_back(bound);
975     }
976     return bounds;
977   }
978   std::optional<std::int64_t>
toInt64(std::optional<Fortran::evaluate::Expr<Fortran::evaluate::SubscriptInteger>> expr)979   toInt64(std::optional<
980           Fortran::evaluate::Expr<Fortran::evaluate::SubscriptInteger>>
981               expr) {
982     if (expr)
983       return Fortran::evaluate::ToInt64(Fortran::evaluate::Fold(
984           getConverter().getFoldingContext(), toEvExpr(*expr)));
985     return std::nullopt;
986   }
987   void
addFirOperand(mlir::Type type,int entityPosition,Property p,llvm::ArrayRef<mlir::NamedAttribute> attributes=llvm::None)988   addFirOperand(mlir::Type type, int entityPosition, Property p,
989                 llvm::ArrayRef<mlir::NamedAttribute> attributes = llvm::None) {
990     interface.inputs.emplace_back(
991         FirPlaceHolder{type, entityPosition, p, attributes});
992   }
993   void
addFirResult(mlir::Type type,int entityPosition,Property p,llvm::ArrayRef<mlir::NamedAttribute> attributes=llvm::None)994   addFirResult(mlir::Type type, int entityPosition, Property p,
995                llvm::ArrayRef<mlir::NamedAttribute> attributes = llvm::None) {
996     interface.outputs.emplace_back(
997         FirPlaceHolder{type, entityPosition, p, attributes});
998   }
addPassedArg(PassEntityBy p,FortranEntity entity,const DummyCharacteristics * characteristics)999   void addPassedArg(PassEntityBy p, FortranEntity entity,
1000                     const DummyCharacteristics *characteristics) {
1001     interface.passedArguments.emplace_back(
1002         PassedEntity{p, entity, emptyValue(), emptyValue(), characteristics});
1003   }
setPassedResult(PassEntityBy p,FortranEntity entity)1004   void setPassedResult(PassEntityBy p, FortranEntity entity) {
1005     interface.passedResult =
1006         PassedEntity{p, entity, emptyValue(), emptyValue()};
1007   }
setSaveResult()1008   void setSaveResult() { interface.saveResult = true; }
nextPassedArgPosition()1009   int nextPassedArgPosition() { return interface.passedArguments.size(); }
1010 
emptyValue()1011   static FirValue emptyValue() {
1012     if constexpr (std::is_same_v<Fortran::lower::CalleeInterface, T>) {
1013       return {};
1014     } else {
1015       return -1;
1016     }
1017   }
1018 
getConverter()1019   Fortran::lower::AbstractConverter &getConverter() {
1020     return interface.converter;
1021   }
1022   CallInterface &interface;
1023   mlir::MLIRContext &mlirContext;
1024 };
1025 
1026 template <typename T>
isOptional() const1027 bool Fortran::lower::CallInterface<T>::PassedEntity::isOptional() const {
1028   if (!characteristics)
1029     return false;
1030   return characteristics->IsOptional();
1031 }
1032 template <typename T>
mayBeModifiedByCall() const1033 bool Fortran::lower::CallInterface<T>::PassedEntity::mayBeModifiedByCall()
1034     const {
1035   if (!characteristics)
1036     return true;
1037   return characteristics->GetIntent() != Fortran::common::Intent::In;
1038 }
1039 template <typename T>
mayBeReadByCall() const1040 bool Fortran::lower::CallInterface<T>::PassedEntity::mayBeReadByCall() const {
1041   if (!characteristics)
1042     return true;
1043   return characteristics->GetIntent() != Fortran::common::Intent::Out;
1044 }
1045 
1046 template <typename T>
determineInterface(bool isImplicit,const Fortran::evaluate::characteristics::Procedure & procedure)1047 void Fortran::lower::CallInterface<T>::determineInterface(
1048     bool isImplicit,
1049     const Fortran::evaluate::characteristics::Procedure &procedure) {
1050   CallInterfaceImpl<T> impl(*this);
1051   if (isImplicit)
1052     impl.buildImplicitInterface(procedure);
1053   else
1054     impl.buildExplicitInterface(procedure);
1055   // We only expect the extra host asspciations argument from the callee side as
1056   // the definition of internal procedures will be present, and we'll always
1057   // have a FuncOp definition in the ModuleOp, when lowering.
1058   if constexpr (std::is_same_v<T, Fortran::lower::CalleeInterface>) {
1059     if (side().hasHostAssociated())
1060       impl.appendHostAssocTupleArg(side().getHostAssociatedTy());
1061   }
1062 }
1063 
1064 template <typename T>
genFunctionType()1065 mlir::FunctionType Fortran::lower::CallInterface<T>::genFunctionType() {
1066   llvm::SmallVector<mlir::Type> returnTys;
1067   llvm::SmallVector<mlir::Type> inputTys;
1068   for (const FirPlaceHolder &placeHolder : outputs)
1069     returnTys.emplace_back(placeHolder.type);
1070   for (const FirPlaceHolder &placeHolder : inputs)
1071     inputTys.emplace_back(placeHolder.type);
1072   return mlir::FunctionType::get(&converter.getMLIRContext(), inputTys,
1073                                  returnTys);
1074 }
1075 
1076 template <typename T>
1077 llvm::SmallVector<mlir::Type>
getResultType() const1078 Fortran::lower::CallInterface<T>::getResultType() const {
1079   llvm::SmallVector<mlir::Type> types;
1080   for (const FirPlaceHolder &out : outputs)
1081     types.emplace_back(out.type);
1082   return types;
1083 }
1084 
1085 template class Fortran::lower::CallInterface<Fortran::lower::CalleeInterface>;
1086 template class Fortran::lower::CallInterface<Fortran::lower::CallerInterface>;
1087 
1088 //===----------------------------------------------------------------------===//
1089 // Function Type Translation
1090 //===----------------------------------------------------------------------===//
1091 
1092 /// Build signature from characteristics when there is no Fortran entity to
1093 /// associate with the arguments (i.e, this is not a call site or a procedure
1094 /// declaration. This is needed when dealing with function pointers/dummy
1095 /// arguments.
1096 
1097 class SignatureBuilder;
1098 template <>
1099 struct Fortran::lower::PassedEntityTypes<SignatureBuilder> {
1100   using FortranEntity = FakeEntity;
1101   using FirValue = int;
1102 };
1103 
1104 /// SignatureBuilder is a CRTP implementation of CallInterface intended to
1105 /// help translating characteristics::Procedure to mlir::FunctionType using
1106 /// the CallInterface translation.
1107 class SignatureBuilder
1108     : public Fortran::lower::CallInterface<SignatureBuilder> {
1109 public:
SignatureBuilder(const Fortran::evaluate::characteristics::Procedure & p,Fortran::lower::AbstractConverter & c,bool forceImplicit)1110   SignatureBuilder(const Fortran::evaluate::characteristics::Procedure &p,
1111                    Fortran::lower::AbstractConverter &c, bool forceImplicit)
1112       : CallInterface{c}, proc{p} {
1113     bool isImplicit = forceImplicit || proc.CanBeCalledViaImplicitInterface();
1114     determineInterface(isImplicit, proc);
1115   }
1116   /// Does the procedure characteristics being translated have alternate
1117   /// returns ?
hasAlternateReturns() const1118   bool hasAlternateReturns() const {
1119     for (const Fortran::evaluate::characteristics::DummyArgument &dummy :
1120          proc.dummyArguments)
1121       if (std::holds_alternative<
1122               Fortran::evaluate::characteristics::AlternateReturn>(dummy.u))
1123         return true;
1124     return false;
1125   };
1126 
1127   /// This is only here to fulfill CRTP dependencies and should not be called.
getMangledName() const1128   std::string getMangledName() const {
1129     llvm_unreachable("trying to get name from SignatureBuilder");
1130   }
1131 
1132   /// This is only here to fulfill CRTP dependencies and should not be called.
getCalleeLocation() const1133   mlir::Location getCalleeLocation() const {
1134     llvm_unreachable("trying to get callee location from SignatureBuilder");
1135   }
1136 
1137   /// This is only here to fulfill CRTP dependencies and should not be called.
getProcedureSymbol() const1138   const Fortran::semantics::Symbol *getProcedureSymbol() const {
1139     llvm_unreachable("trying to get callee symbol from SignatureBuilder");
1140   };
1141 
characterize() const1142   Fortran::evaluate::characteristics::Procedure characterize() const {
1143     return proc;
1144   }
1145   /// SignatureBuilder cannot be used on main program.
isMainProgram()1146   static constexpr bool isMainProgram() { return false; }
1147 
1148   /// Return the characteristics::Procedure that is being translated to
1149   /// mlir::FunctionType.
1150   const Fortran::evaluate::characteristics::Procedure &
getCallDescription() const1151   getCallDescription() const {
1152     return proc;
1153   }
1154 
1155   /// This is not the description of an indirect call.
isIndirectCall()1156   static constexpr bool isIndirectCall() { return false; }
1157 
1158   /// Return the translated signature.
getFunctionType()1159   mlir::FunctionType getFunctionType() { return genFunctionType(); }
1160 
1161   // Copy of base implementation.
hasHostAssociated()1162   static constexpr bool hasHostAssociated() { return false; }
getHostAssociatedTy() const1163   mlir::Type getHostAssociatedTy() const {
1164     llvm_unreachable("getting host associated type in SignatureBuilder");
1165   }
1166 
1167 private:
1168   const Fortran::evaluate::characteristics::Procedure &proc;
1169 };
1170 
translateSignature(const Fortran::evaluate::ProcedureDesignator & proc,Fortran::lower::AbstractConverter & converter)1171 mlir::FunctionType Fortran::lower::translateSignature(
1172     const Fortran::evaluate::ProcedureDesignator &proc,
1173     Fortran::lower::AbstractConverter &converter) {
1174   std::optional<Fortran::evaluate::characteristics::Procedure> characteristics =
1175       Fortran::evaluate::characteristics::Procedure::Characterize(
1176           proc, converter.getFoldingContext());
1177   // Most unrestricted intrinsic characteristic has the Elemental attribute
1178   // which triggers CanBeCalledViaImplicitInterface to return false. However,
1179   // using implicit interface rules is just fine here.
1180   bool forceImplicit = proc.GetSpecificIntrinsic();
1181   return SignatureBuilder{characteristics.value(), converter, forceImplicit}
1182       .getFunctionType();
1183 }
1184 
getOrDeclareFunction(llvm::StringRef name,const Fortran::evaluate::ProcedureDesignator & proc,Fortran::lower::AbstractConverter & converter)1185 mlir::func::FuncOp Fortran::lower::getOrDeclareFunction(
1186     llvm::StringRef name, const Fortran::evaluate::ProcedureDesignator &proc,
1187     Fortran::lower::AbstractConverter &converter) {
1188   mlir::ModuleOp module = converter.getModuleOp();
1189   mlir::func::FuncOp func = fir::FirOpBuilder::getNamedFunction(module, name);
1190   if (func)
1191     return func;
1192 
1193   const Fortran::semantics::Symbol *symbol = proc.GetSymbol();
1194   assert(symbol && "non user function in getOrDeclareFunction");
1195   // getOrDeclareFunction is only used for functions not defined in the current
1196   // program unit, so use the location of the procedure designator symbol, which
1197   // is the first occurrence of the procedure in the program unit.
1198   mlir::Location loc = converter.genLocation(symbol->name());
1199   std::optional<Fortran::evaluate::characteristics::Procedure> characteristics =
1200       Fortran::evaluate::characteristics::Procedure::Characterize(
1201           proc, converter.getFoldingContext());
1202   mlir::FunctionType ty = SignatureBuilder{characteristics.value(), converter,
1203                                            /*forceImplicit=*/false}
1204                               .getFunctionType();
1205   mlir::func::FuncOp newFunc =
1206       fir::FirOpBuilder::createFunction(loc, module, name, ty);
1207   addSymbolAttribute(newFunc, *symbol, converter.getMLIRContext());
1208   return newFunc;
1209 }
1210 
1211 // Is it required to pass a dummy procedure with \p characteristics as a tuple
1212 // containing the function address and the result length ?
mustPassLengthWithDummyProcedure(const std::optional<Fortran::evaluate::characteristics::Procedure> & characteristics)1213 static bool mustPassLengthWithDummyProcedure(
1214     const std::optional<Fortran::evaluate::characteristics::Procedure>
1215         &characteristics) {
1216   return characteristics &&
1217          Fortran::lower::CallInterfaceImpl<SignatureBuilder>::
1218              mustPassLengthWithDummyProcedure(*characteristics);
1219 }
1220 
mustPassLengthWithDummyProcedure(const Fortran::evaluate::ProcedureDesignator & procedure,Fortran::lower::AbstractConverter & converter)1221 bool Fortran::lower::mustPassLengthWithDummyProcedure(
1222     const Fortran::evaluate::ProcedureDesignator &procedure,
1223     Fortran::lower::AbstractConverter &converter) {
1224   std::optional<Fortran::evaluate::characteristics::Procedure> characteristics =
1225       Fortran::evaluate::characteristics::Procedure::Characterize(
1226           procedure, converter.getFoldingContext());
1227   return ::mustPassLengthWithDummyProcedure(characteristics);
1228 }
1229 
getDummyProcedureType(const Fortran::semantics::Symbol & dummyProc,Fortran::lower::AbstractConverter & converter)1230 mlir::Type Fortran::lower::getDummyProcedureType(
1231     const Fortran::semantics::Symbol &dummyProc,
1232     Fortran::lower::AbstractConverter &converter) {
1233   std::optional<Fortran::evaluate::characteristics::Procedure> iface =
1234       Fortran::evaluate::characteristics::Procedure::Characterize(
1235           dummyProc, converter.getFoldingContext());
1236   mlir::Type procType = getProcedureDesignatorType(
1237       iface.has_value() ? &*iface : nullptr, converter);
1238   if (::mustPassLengthWithDummyProcedure(iface))
1239     return fir::factory::getCharacterProcedureTupleType(procType);
1240   return procType;
1241 }
1242