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. 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). 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 65 bool Fortran::lower::CallerInterface::hasAlternateReturns() const { 66 return procRef.hasAlternateReturns(); 67 } 68 69 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 * 80 Fortran::lower::CallerInterface::getProcedureSymbol() const { 81 return procRef.proc().GetSymbol(); 82 } 83 84 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 * 92 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 100 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 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 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 145 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 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 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 200 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 210 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 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 245 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 267 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 283 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 300 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 & 308 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 * 319 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 331 bool Fortran::lower::CalleeInterface::hasAlternateReturns() const { 332 return !funit.isMainProgram() && 333 Fortran::semantics::HasAlternateReturns(funit.getSubprogramSymbol()); 334 } 335 336 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 * 344 Fortran::lower::CalleeInterface::getProcedureSymbol() const { 345 if (funit.isMainProgram()) 346 return nullptr; 347 return &funit.getSubprogramSymbol(); 348 } 349 350 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 357 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 367 bool Fortran::lower::CalleeInterface::isMainProgram() const { 368 return funit.isMainProgram(); 369 } 370 371 mlir::func::FuncOp 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 386 bool Fortran::lower::CalleeInterface::hasHostAssociated() const { 387 return funit.parentHasHostAssoc(); 388 } 389 390 mlir::Type Fortran::lower::CalleeInterface::getHostAssociatedTy() const { 391 assert(hasHostAssociated()); 392 return funit.parentHostAssoc().getArgumentType(converter); 393 } 394 395 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 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> 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> 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> 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 & 484 getEntityContainer(const Fortran::evaluate::ProcedureRef &proc) { 485 return proc.arguments(); 486 } 487 488 static const std::vector<Fortran::semantics::Symbol *> & 489 getEntityContainer(Fortran::lower::pft::FunctionLikeUnit &funit) { 490 return funit.getSubprogramSymbol() 491 .get<Fortran::semantics::SubprogramDetails>() 492 .dummyArgs(); 493 } 494 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 & 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 * 509 getResultEntity(const Fortran::evaluate::ProcedureRef &) { 510 return nullptr; 511 } 512 513 static const Fortran::semantics::Symbol & 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 525 getEntityContainer(const Fortran::evaluate::characteristics::Procedure &proc) { 526 FakeEntities enities(proc.dummyArguments.size()); 527 return enities; 528 } 529 static const FakeEntity &getDataObjectEntity(const FakeEntity &e) { return e; } 530 static FakeEntity 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: 551 CallInterfaceImpl(CallInterface &i) 552 : interface(i), mlirContext{i.converter.getMLIRContext()} {} 553 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 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 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 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 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: 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 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> 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 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. 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 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 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 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 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 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> 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 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 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 } 999 void addPassedArg(PassEntityBy p, FortranEntity entity, 1000 const DummyCharacteristics *characteristics) { 1001 interface.passedArguments.emplace_back( 1002 PassedEntity{p, entity, emptyValue(), emptyValue(), characteristics}); 1003 } 1004 void setPassedResult(PassEntityBy p, FortranEntity entity) { 1005 interface.passedResult = 1006 PassedEntity{p, entity, emptyValue(), emptyValue()}; 1007 } 1008 void setSaveResult() { interface.saveResult = true; } 1009 int nextPassedArgPosition() { return interface.passedArguments.size(); } 1010 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 1019 Fortran::lower::AbstractConverter &getConverter() { 1020 return interface.converter; 1021 } 1022 CallInterface &interface; 1023 mlir::MLIRContext &mlirContext; 1024 }; 1025 1026 template <typename T> 1027 bool Fortran::lower::CallInterface<T>::PassedEntity::isOptional() const { 1028 if (!characteristics) 1029 return false; 1030 return characteristics->IsOptional(); 1031 } 1032 template <typename T> 1033 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> 1040 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> 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> 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> 1078 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: 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 ? 1118 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. 1128 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. 1133 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. 1138 const Fortran::semantics::Symbol *getProcedureSymbol() const { 1139 llvm_unreachable("trying to get callee symbol from SignatureBuilder"); 1140 }; 1141 1142 Fortran::evaluate::characteristics::Procedure characterize() const { 1143 return proc; 1144 } 1145 /// SignatureBuilder cannot be used on main program. 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 & 1151 getCallDescription() const { 1152 return proc; 1153 } 1154 1155 /// This is not the description of an indirect call. 1156 static constexpr bool isIndirectCall() { return false; } 1157 1158 /// Return the translated signature. 1159 mlir::FunctionType getFunctionType() { return genFunctionType(); } 1160 1161 // Copy of base implementation. 1162 static constexpr bool hasHostAssociated() { return false; } 1163 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 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 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 ? 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 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 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