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