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