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/Support/Utils.h" 15 #include "flang/Lower/Todo.h" 16 #include "flang/Optimizer/Builder/FIRBuilder.h" 17 #include "flang/Optimizer/Dialect/FIRDialect.h" 18 #include "flang/Optimizer/Dialect/FIROpsSupport.h" 19 #include "flang/Optimizer/Support/InternalNames.h" 20 #include "flang/Semantics/symbol.h" 21 #include "flang/Semantics/tools.h" 22 23 //===----------------------------------------------------------------------===// 24 // BIND(C) mangling helpers 25 //===----------------------------------------------------------------------===// 26 27 // Return the binding label (from BIND(C...)) or the mangled name of a symbol. 28 static std::string getMangledName(const Fortran::semantics::Symbol &symbol) { 29 const std::string *bindName = symbol.GetBindName(); 30 return bindName ? *bindName : Fortran::lower::mangle::mangleName(symbol); 31 } 32 33 //===----------------------------------------------------------------------===// 34 // Caller side interface implementation 35 //===----------------------------------------------------------------------===// 36 37 bool Fortran::lower::CallerInterface::hasAlternateReturns() const { 38 return procRef.hasAlternateReturns(); 39 } 40 41 std::string Fortran::lower::CallerInterface::getMangledName() const { 42 const Fortran::evaluate::ProcedureDesignator &proc = procRef.proc(); 43 if (const Fortran::semantics::Symbol *symbol = proc.GetSymbol()) 44 return ::getMangledName(symbol->GetUltimate()); 45 assert(proc.GetSpecificIntrinsic() && 46 "expected intrinsic procedure in designator"); 47 return proc.GetName(); 48 } 49 50 const Fortran::semantics::Symbol * 51 Fortran::lower::CallerInterface::getProcedureSymbol() const { 52 return procRef.proc().GetSymbol(); 53 } 54 55 bool Fortran::lower::CallerInterface::isIndirectCall() const { 56 if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol()) 57 return Fortran::semantics::IsPointer(*symbol) || 58 Fortran::semantics::IsDummy(*symbol); 59 return false; 60 } 61 62 const Fortran::semantics::Symbol * 63 Fortran::lower::CallerInterface::getIfIndirectCallSymbol() const { 64 if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol()) 65 if (Fortran::semantics::IsPointer(*symbol) || 66 Fortran::semantics::IsDummy(*symbol)) 67 return symbol; 68 return nullptr; 69 } 70 71 mlir::Location Fortran::lower::CallerInterface::getCalleeLocation() const { 72 const Fortran::evaluate::ProcedureDesignator &proc = procRef.proc(); 73 // FIXME: If the callee is defined in the same file but after the current 74 // unit we cannot get its location here and the funcOp is created at the 75 // wrong location (i.e, the caller location). 76 if (const Fortran::semantics::Symbol *symbol = proc.GetSymbol()) 77 return converter.genLocation(symbol->name()); 78 // Use current location for intrinsics. 79 return converter.getCurrentLocation(); 80 } 81 82 // Get dummy argument characteristic for a procedure with implicit interface 83 // from the actual argument characteristic. The actual argument may not be a F77 84 // entity. The attribute must be dropped and the shape, if any, must be made 85 // explicit. 86 static Fortran::evaluate::characteristics::DummyDataObject 87 asImplicitArg(Fortran::evaluate::characteristics::DummyDataObject &&dummy) { 88 Fortran::evaluate::Shape shape = 89 dummy.type.attrs().none() ? dummy.type.shape() 90 : Fortran::evaluate::Shape(dummy.type.Rank()); 91 return Fortran::evaluate::characteristics::DummyDataObject( 92 Fortran::evaluate::characteristics::TypeAndShape(dummy.type.type(), 93 std::move(shape))); 94 } 95 96 static Fortran::evaluate::characteristics::DummyArgument 97 asImplicitArg(Fortran::evaluate::characteristics::DummyArgument &&dummy) { 98 return std::visit( 99 Fortran::common::visitors{ 100 [&](Fortran::evaluate::characteristics::DummyDataObject &obj) { 101 return Fortran::evaluate::characteristics::DummyArgument( 102 std::move(dummy.name), asImplicitArg(std::move(obj))); 103 }, 104 [&](Fortran::evaluate::characteristics::DummyProcedure &proc) { 105 return Fortran::evaluate::characteristics::DummyArgument( 106 std::move(dummy.name), std::move(proc)); 107 }, 108 [](Fortran::evaluate::characteristics::AlternateReturn &x) { 109 return Fortran::evaluate::characteristics::DummyArgument( 110 std::move(x)); 111 }}, 112 dummy.u); 113 } 114 115 Fortran::evaluate::characteristics::Procedure 116 Fortran::lower::CallerInterface::characterize() const { 117 Fortran::evaluate::FoldingContext &foldingContext = 118 converter.getFoldingContext(); 119 std::optional<Fortran::evaluate::characteristics::Procedure> characteristic = 120 Fortran::evaluate::characteristics::Procedure::Characterize( 121 procRef.proc(), foldingContext); 122 assert(characteristic && "Failed to get characteristic from procRef"); 123 // The characteristic may not contain the argument characteristic if the 124 // ProcedureDesignator has no interface. 125 if (!characteristic->HasExplicitInterface()) { 126 for (const std::optional<Fortran::evaluate::ActualArgument> &arg : 127 procRef.arguments()) { 128 if (arg.value().isAlternateReturn()) { 129 characteristic->dummyArguments.emplace_back( 130 Fortran::evaluate::characteristics::AlternateReturn{}); 131 } else { 132 // Argument cannot be optional with implicit interface 133 const Fortran::lower::SomeExpr *expr = arg.value().UnwrapExpr(); 134 assert( 135 expr && 136 "argument in call with implicit interface cannot be assumed type"); 137 std::optional<Fortran::evaluate::characteristics::DummyArgument> 138 argCharacteristic = 139 Fortran::evaluate::characteristics::DummyArgument::FromActual( 140 "actual", *expr, foldingContext); 141 assert(argCharacteristic && 142 "failed to characterize argument in implicit call"); 143 characteristic->dummyArguments.emplace_back( 144 asImplicitArg(std::move(*argCharacteristic))); 145 } 146 } 147 } 148 return *characteristic; 149 } 150 151 void Fortran::lower::CallerInterface::placeInput( 152 const PassedEntity &passedEntity, mlir::Value arg) { 153 assert(static_cast<int>(actualInputs.size()) > passedEntity.firArgument && 154 passedEntity.firArgument >= 0 && 155 passedEntity.passBy != CallInterface::PassEntityBy::AddressAndLength && 156 "bad arg position"); 157 actualInputs[passedEntity.firArgument] = arg; 158 } 159 160 void Fortran::lower::CallerInterface::placeAddressAndLengthInput( 161 const PassedEntity &passedEntity, mlir::Value addr, mlir::Value len) { 162 assert(static_cast<int>(actualInputs.size()) > passedEntity.firArgument && 163 static_cast<int>(actualInputs.size()) > passedEntity.firLength && 164 passedEntity.firArgument >= 0 && passedEntity.firLength >= 0 && 165 passedEntity.passBy == CallInterface::PassEntityBy::AddressAndLength && 166 "bad arg position"); 167 actualInputs[passedEntity.firArgument] = addr; 168 actualInputs[passedEntity.firLength] = len; 169 } 170 171 bool Fortran::lower::CallerInterface::verifyActualInputs() const { 172 if (getNumFIRArguments() != actualInputs.size()) 173 return false; 174 for (mlir::Value arg : actualInputs) { 175 if (!arg) 176 return false; 177 } 178 return true; 179 } 180 181 void Fortran::lower::CallerInterface::walkResultLengths( 182 ExprVisitor visitor) const { 183 assert(characteristic && "characteristic was not computed"); 184 const Fortran::evaluate::characteristics::FunctionResult &result = 185 characteristic->functionResult.value(); 186 const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape = 187 result.GetTypeAndShape(); 188 assert(typeAndShape && "no result type"); 189 Fortran::evaluate::DynamicType dynamicType = typeAndShape->type(); 190 // Visit result length specification expressions that are explicit. 191 if (dynamicType.category() == Fortran::common::TypeCategory::Character) { 192 if (std::optional<Fortran::evaluate::ExtentExpr> length = 193 dynamicType.GetCharLength()) 194 visitor(toEvExpr(*length)); 195 } else if (dynamicType.category() == common::TypeCategory::Derived) { 196 const Fortran::semantics::DerivedTypeSpec &derivedTypeSpec = 197 dynamicType.GetDerivedTypeSpec(); 198 if (Fortran::semantics::CountLenParameters(derivedTypeSpec) > 0) 199 TODO(converter.getCurrentLocation(), 200 "function result with derived type length parameters"); 201 } 202 } 203 204 // Compute extent expr from shapeSpec of an explicit shape. 205 // TODO: Allow evaluate shape analysis to work in a mode where it disregards 206 // the non-constant aspects when building the shape to avoid having this here. 207 static Fortran::evaluate::ExtentExpr 208 getExtentExpr(const Fortran::semantics::ShapeSpec &shapeSpec) { 209 const auto &ubound = shapeSpec.ubound().GetExplicit(); 210 const auto &lbound = shapeSpec.lbound().GetExplicit(); 211 assert(lbound && ubound && "shape must be explicit"); 212 return Fortran::common::Clone(*ubound) - Fortran::common::Clone(*lbound) + 213 Fortran::evaluate::ExtentExpr{1}; 214 } 215 216 void Fortran::lower::CallerInterface::walkResultExtents( 217 ExprVisitor visitor) const { 218 // Walk directly the result symbol shape (the characteristic shape may contain 219 // descriptor inquiries to it that would fail to lower on the caller side). 220 const Fortran::semantics::Symbol *interfaceSymbol = 221 procRef.proc().GetInterfaceSymbol(); 222 if (interfaceSymbol) { 223 const Fortran::semantics::Symbol &result = 224 interfaceSymbol->get<Fortran::semantics::SubprogramDetails>().result(); 225 if (const auto *objectDetails = 226 result.detailsIf<Fortran::semantics::ObjectEntityDetails>()) 227 if (objectDetails->shape().IsExplicitShape()) 228 for (const Fortran::semantics::ShapeSpec &shapeSpec : 229 objectDetails->shape()) 230 visitor(Fortran::evaluate::AsGenericExpr(getExtentExpr(shapeSpec))); 231 } else { 232 if (procRef.Rank() != 0) 233 fir::emitFatalError( 234 converter.getCurrentLocation(), 235 "only scalar functions may not have an interface symbol"); 236 } 237 } 238 239 bool Fortran::lower::CallerInterface::mustMapInterfaceSymbols() const { 240 assert(characteristic && "characteristic was not computed"); 241 const std::optional<Fortran::evaluate::characteristics::FunctionResult> 242 &result = characteristic->functionResult; 243 if (!result || result->CanBeReturnedViaImplicitInterface() || 244 !procRef.proc().GetInterfaceSymbol()) 245 return false; 246 bool allResultSpecExprConstant = true; 247 auto visitor = [&](const Fortran::lower::SomeExpr &e) { 248 allResultSpecExprConstant &= Fortran::evaluate::IsConstantExpr(e); 249 }; 250 walkResultLengths(visitor); 251 walkResultExtents(visitor); 252 return !allResultSpecExprConstant; 253 } 254 255 mlir::Value Fortran::lower::CallerInterface::getArgumentValue( 256 const semantics::Symbol &sym) const { 257 mlir::Location loc = converter.getCurrentLocation(); 258 const Fortran::semantics::Symbol *iface = procRef.proc().GetInterfaceSymbol(); 259 if (!iface) 260 fir::emitFatalError( 261 loc, "mapping actual and dummy arguments requires an interface"); 262 const std::vector<Fortran::semantics::Symbol *> &dummies = 263 iface->get<semantics::SubprogramDetails>().dummyArgs(); 264 auto it = std::find(dummies.begin(), dummies.end(), &sym); 265 if (it == dummies.end()) 266 fir::emitFatalError(loc, "symbol is not a dummy in this call"); 267 FirValue mlirArgIndex = passedArguments[it - dummies.begin()].firArgument; 268 return actualInputs[mlirArgIndex]; 269 } 270 271 mlir::Type Fortran::lower::CallerInterface::getResultStorageType() const { 272 if (passedResult) 273 return fir::dyn_cast_ptrEleTy(inputs[passedResult->firArgument].type); 274 assert(saveResult && !outputs.empty()); 275 return outputs[0].type; 276 } 277 278 const Fortran::semantics::Symbol & 279 Fortran::lower::CallerInterface::getResultSymbol() const { 280 mlir::Location loc = converter.getCurrentLocation(); 281 const Fortran::semantics::Symbol *iface = procRef.proc().GetInterfaceSymbol(); 282 if (!iface) 283 fir::emitFatalError( 284 loc, "mapping actual and dummy arguments requires an interface"); 285 return iface->get<semantics::SubprogramDetails>().result(); 286 } 287 288 //===----------------------------------------------------------------------===// 289 // Callee side interface implementation 290 //===----------------------------------------------------------------------===// 291 292 bool Fortran::lower::CalleeInterface::hasAlternateReturns() const { 293 return !funit.isMainProgram() && 294 Fortran::semantics::HasAlternateReturns(funit.getSubprogramSymbol()); 295 } 296 297 std::string Fortran::lower::CalleeInterface::getMangledName() const { 298 if (funit.isMainProgram()) 299 return fir::NameUniquer::doProgramEntry().str(); 300 return ::getMangledName(funit.getSubprogramSymbol()); 301 } 302 303 const Fortran::semantics::Symbol * 304 Fortran::lower::CalleeInterface::getProcedureSymbol() const { 305 if (funit.isMainProgram()) 306 return nullptr; 307 return &funit.getSubprogramSymbol(); 308 } 309 310 mlir::Location Fortran::lower::CalleeInterface::getCalleeLocation() const { 311 // FIXME: do NOT use unknown for the anonymous PROGRAM case. We probably 312 // should just stash the location in the funit regardless. 313 return converter.genLocation(funit.getStartingSourceLoc()); 314 } 315 316 Fortran::evaluate::characteristics::Procedure 317 Fortran::lower::CalleeInterface::characterize() const { 318 Fortran::evaluate::FoldingContext &foldingContext = 319 converter.getFoldingContext(); 320 std::optional<Fortran::evaluate::characteristics::Procedure> characteristic = 321 Fortran::evaluate::characteristics::Procedure::Characterize( 322 funit.getSubprogramSymbol(), foldingContext); 323 assert(characteristic && "Fail to get characteristic from symbol"); 324 return *characteristic; 325 } 326 327 bool Fortran::lower::CalleeInterface::isMainProgram() const { 328 return funit.isMainProgram(); 329 } 330 331 mlir::FuncOp Fortran::lower::CalleeInterface::addEntryBlockAndMapArguments() { 332 // On the callee side, directly map the mlir::value argument of 333 // the function block to the Fortran symbols. 334 func.addEntryBlock(); 335 mapPassedEntities(); 336 return func; 337 } 338 339 //===----------------------------------------------------------------------===// 340 // CallInterface implementation: this part is common to both callee and caller 341 // sides. 342 //===----------------------------------------------------------------------===// 343 344 static void addSymbolAttribute(mlir::FuncOp func, 345 const Fortran::semantics::Symbol &sym, 346 mlir::MLIRContext &mlirContext) { 347 // Only add this on bind(C) functions for which the symbol is not reflected in 348 // the current context. 349 if (!Fortran::semantics::IsBindCProcedure(sym)) 350 return; 351 std::string name = 352 Fortran::lower::mangle::mangleName(sym, /*keepExternalInScope=*/true); 353 func->setAttr(fir::getSymbolAttrName(), 354 mlir::StringAttr::get(&mlirContext, name)); 355 } 356 357 /// Declare drives the different actions to be performed while analyzing the 358 /// signature and building/finding the mlir::FuncOp. 359 template <typename T> 360 void Fortran::lower::CallInterface<T>::declare() { 361 if (!side().isMainProgram()) { 362 characteristic.emplace(side().characterize()); 363 bool isImplicit = characteristic->CanBeCalledViaImplicitInterface(); 364 determineInterface(isImplicit, *characteristic); 365 } 366 // No input/output for main program 367 368 // Create / get funcOp for direct calls. For indirect calls (only meaningful 369 // on the caller side), no funcOp has to be created here. The mlir::Value 370 // holding the indirection is used when creating the fir::CallOp. 371 if (!side().isIndirectCall()) { 372 std::string name = side().getMangledName(); 373 mlir::ModuleOp module = converter.getModuleOp(); 374 func = fir::FirOpBuilder::getNamedFunction(module, name); 375 if (!func) { 376 mlir::Location loc = side().getCalleeLocation(); 377 mlir::FunctionType ty = genFunctionType(); 378 func = fir::FirOpBuilder::createFunction(loc, module, name, ty); 379 if (const Fortran::semantics::Symbol *sym = side().getProcedureSymbol()) 380 addSymbolAttribute(func, *sym, converter.getMLIRContext()); 381 for (const auto &placeHolder : llvm::enumerate(inputs)) 382 if (!placeHolder.value().attributes.empty()) 383 func.setArgAttrs(placeHolder.index(), placeHolder.value().attributes); 384 } 385 } 386 } 387 388 /// Once the signature has been analyzed and the mlir::FuncOp was built/found, 389 /// map the fir inputs to Fortran entities (the symbols or expressions). 390 template <typename T> 391 void Fortran::lower::CallInterface<T>::mapPassedEntities() { 392 // map back fir inputs to passed entities 393 if constexpr (std::is_same_v<T, Fortran::lower::CalleeInterface>) { 394 assert(inputs.size() == func.front().getArguments().size() && 395 "function previously created with different number of arguments"); 396 for (auto [fst, snd] : llvm::zip(inputs, func.front().getArguments())) 397 mapBackInputToPassedEntity(fst, snd); 398 } else { 399 // On the caller side, map the index of the mlir argument position 400 // to Fortran ActualArguments. 401 int firPosition = 0; 402 for (const FirPlaceHolder &placeHolder : inputs) 403 mapBackInputToPassedEntity(placeHolder, firPosition++); 404 } 405 } 406 407 template <typename T> 408 void Fortran::lower::CallInterface<T>::mapBackInputToPassedEntity( 409 const FirPlaceHolder &placeHolder, FirValue firValue) { 410 PassedEntity &passedEntity = 411 placeHolder.passedEntityPosition == FirPlaceHolder::resultEntityPosition 412 ? passedResult.value() 413 : passedArguments[placeHolder.passedEntityPosition]; 414 if (placeHolder.property == Property::CharLength) 415 passedEntity.firLength = firValue; 416 else 417 passedEntity.firArgument = firValue; 418 } 419 420 /// Helpers to access ActualArgument/Symbols 421 static const Fortran::evaluate::ActualArguments & 422 getEntityContainer(const Fortran::evaluate::ProcedureRef &proc) { 423 return proc.arguments(); 424 } 425 426 static const std::vector<Fortran::semantics::Symbol *> & 427 getEntityContainer(Fortran::lower::pft::FunctionLikeUnit &funit) { 428 return funit.getSubprogramSymbol() 429 .get<Fortran::semantics::SubprogramDetails>() 430 .dummyArgs(); 431 } 432 433 static const Fortran::evaluate::ActualArgument *getDataObjectEntity( 434 const std::optional<Fortran::evaluate::ActualArgument> &arg) { 435 if (arg) 436 return &*arg; 437 return nullptr; 438 } 439 440 static const Fortran::semantics::Symbol & 441 getDataObjectEntity(const Fortran::semantics::Symbol *arg) { 442 assert(arg && "expect symbol for data object entity"); 443 return *arg; 444 } 445 446 //===----------------------------------------------------------------------===// 447 // CallInterface implementation: this part is common to both caller and caller 448 // sides. 449 //===----------------------------------------------------------------------===// 450 451 /// This is the actual part that defines the FIR interface based on the 452 /// characteristic. It directly mutates the CallInterface members. 453 template <typename T> 454 class Fortran::lower::CallInterfaceImpl { 455 using CallInterface = Fortran::lower::CallInterface<T>; 456 using PassEntityBy = typename CallInterface::PassEntityBy; 457 using PassedEntity = typename CallInterface::PassedEntity; 458 using FortranEntity = typename CallInterface::FortranEntity; 459 using FirPlaceHolder = typename CallInterface::FirPlaceHolder; 460 using Property = typename CallInterface::Property; 461 using TypeAndShape = Fortran::evaluate::characteristics::TypeAndShape; 462 using DummyCharacteristics = 463 Fortran::evaluate::characteristics::DummyArgument; 464 465 public: 466 CallInterfaceImpl(CallInterface &i) 467 : interface(i), mlirContext{i.converter.getMLIRContext()} {} 468 469 void buildImplicitInterface( 470 const Fortran::evaluate::characteristics::Procedure &procedure) { 471 // Handle result 472 if (const std::optional<Fortran::evaluate::characteristics::FunctionResult> 473 &result = procedure.functionResult) 474 handleImplicitResult(*result); 475 else if (interface.side().hasAlternateReturns()) 476 addFirResult(mlir::IndexType::get(&mlirContext), 477 FirPlaceHolder::resultEntityPosition, Property::Value); 478 // Handle arguments 479 const auto &argumentEntities = 480 getEntityContainer(interface.side().getCallDescription()); 481 for (auto pair : llvm::zip(procedure.dummyArguments, argumentEntities)) { 482 const Fortran::evaluate::characteristics::DummyArgument 483 &argCharacteristics = std::get<0>(pair); 484 std::visit( 485 Fortran::common::visitors{ 486 [&](const auto &dummy) { 487 const auto &entity = getDataObjectEntity(std::get<1>(pair)); 488 handleImplicitDummy(&argCharacteristics, dummy, entity); 489 }, 490 [&](const Fortran::evaluate::characteristics::AlternateReturn &) { 491 // nothing to do 492 }, 493 }, 494 argCharacteristics.u); 495 } 496 } 497 498 void buildExplicitInterface( 499 const Fortran::evaluate::characteristics::Procedure &procedure) { 500 // Handle result 501 if (const std::optional<Fortran::evaluate::characteristics::FunctionResult> 502 &result = procedure.functionResult) { 503 if (result->CanBeReturnedViaImplicitInterface()) 504 handleImplicitResult(*result); 505 else 506 handleExplicitResult(*result); 507 } else if (interface.side().hasAlternateReturns()) { 508 addFirResult(mlir::IndexType::get(&mlirContext), 509 FirPlaceHolder::resultEntityPosition, Property::Value); 510 } 511 } 512 513 private: 514 void handleImplicitResult( 515 const Fortran::evaluate::characteristics::FunctionResult &result) { 516 if (result.IsProcedurePointer()) 517 TODO(interface.converter.getCurrentLocation(), 518 "procedure pointer result not yet handled"); 519 const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape = 520 result.GetTypeAndShape(); 521 assert(typeAndShape && "expect type for non proc pointer result"); 522 Fortran::evaluate::DynamicType dynamicType = typeAndShape->type(); 523 if (dynamicType.category() == Fortran::common::TypeCategory::Character) { 524 TODO(interface.converter.getCurrentLocation(), 525 "implicit result character type"); 526 } else if (dynamicType.category() == 527 Fortran::common::TypeCategory::Derived) { 528 TODO(interface.converter.getCurrentLocation(), 529 "implicit result derived type"); 530 } else { 531 // All result other than characters/derived are simply returned by value 532 // in implicit interfaces 533 mlir::Type mlirType = 534 getConverter().genType(dynamicType.category(), dynamicType.kind()); 535 addFirResult(mlirType, FirPlaceHolder::resultEntityPosition, 536 Property::Value); 537 } 538 } 539 540 void handleExplicitResult( 541 const Fortran::evaluate::characteristics::FunctionResult &result) { 542 using Attr = Fortran::evaluate::characteristics::FunctionResult::Attr; 543 544 if (result.IsProcedurePointer()) 545 TODO(interface.converter.getCurrentLocation(), 546 "procedure pointer results"); 547 const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape = 548 result.GetTypeAndShape(); 549 assert(typeAndShape && "expect type for non proc pointer result"); 550 Fortran::evaluate::DynamicType dynamicType = typeAndShape->type(); 551 if (dynamicType.category() == Fortran::common::TypeCategory::Character) { 552 TODO(interface.converter.getCurrentLocation(), 553 "implicit result character type"); 554 } else if (dynamicType.category() == 555 Fortran::common::TypeCategory::Derived) { 556 TODO(interface.converter.getCurrentLocation(), 557 "implicit result derived type"); 558 } 559 mlir::Type mlirType = 560 getConverter().genType(dynamicType.category(), dynamicType.kind()); 561 fir::SequenceType::Shape bounds = getBounds(typeAndShape->shape()); 562 if (!bounds.empty()) 563 mlirType = fir::SequenceType::get(bounds, mlirType); 564 if (result.attrs.test(Attr::Allocatable)) 565 mlirType = fir::BoxType::get(fir::HeapType::get(mlirType)); 566 if (result.attrs.test(Attr::Pointer)) 567 mlirType = fir::BoxType::get(fir::PointerType::get(mlirType)); 568 569 addFirResult(mlirType, FirPlaceHolder::resultEntityPosition, 570 Property::Value); 571 } 572 573 fir::SequenceType::Shape getBounds(const Fortran::evaluate::Shape &shape) { 574 fir::SequenceType::Shape bounds; 575 for (Fortran::evaluate::MaybeExtentExpr extentExpr : shape) { 576 fir::SequenceType::Extent extent = fir::SequenceType::getUnknownExtent(); 577 if (std::optional<std::int64_t> constantExtent = 578 toInt64(std::move(extentExpr))) 579 extent = *constantExtent; 580 bounds.push_back(extent); 581 } 582 return bounds; 583 } 584 585 template <typename A> 586 std::optional<std::int64_t> toInt64(A &&expr) { 587 return Fortran::evaluate::ToInt64(Fortran::evaluate::Fold( 588 getConverter().getFoldingContext(), std::move(expr))); 589 } 590 591 /// Return a vector with an attribute with the name of the argument if this 592 /// is a callee interface and the name is available. Otherwise, just return 593 /// an empty vector. 594 llvm::SmallVector<mlir::NamedAttribute> 595 dummyNameAttr(const FortranEntity &entity) { 596 if constexpr (std::is_same_v<FortranEntity, 597 std::optional<Fortran::common::Reference< 598 const Fortran::semantics::Symbol>>>) { 599 if (entity.has_value()) { 600 const Fortran::semantics::Symbol *argument = &*entity.value(); 601 // "fir.bindc_name" is used for arguments for the sake of consistency 602 // with other attributes carrying surface syntax names in FIR. 603 return {mlir::NamedAttribute( 604 mlir::StringAttr::get(&mlirContext, "fir.bindc_name"), 605 mlir::StringAttr::get(&mlirContext, 606 toStringRef(argument->name())))}; 607 } 608 } 609 return {}; 610 } 611 612 void handleImplicitDummy( 613 const DummyCharacteristics *characteristics, 614 const Fortran::evaluate::characteristics::DummyDataObject &obj, 615 const FortranEntity &entity) { 616 Fortran::evaluate::DynamicType dynamicType = obj.type.type(); 617 if (dynamicType.category() == Fortran::common::TypeCategory::Character) { 618 mlir::Type boxCharTy = 619 fir::BoxCharType::get(&mlirContext, dynamicType.kind()); 620 addFirOperand(boxCharTy, nextPassedArgPosition(), Property::BoxChar, 621 dummyNameAttr(entity)); 622 addPassedArg(PassEntityBy::BoxChar, entity, characteristics); 623 } else { 624 // non-PDT derived type allowed in implicit interface. 625 Fortran::common::TypeCategory cat = dynamicType.category(); 626 mlir::Type type = getConverter().genType(cat, dynamicType.kind()); 627 fir::SequenceType::Shape bounds = getBounds(obj.type.shape()); 628 if (!bounds.empty()) 629 type = fir::SequenceType::get(bounds, type); 630 mlir::Type refType = fir::ReferenceType::get(type); 631 addFirOperand(refType, nextPassedArgPosition(), Property::BaseAddress, 632 dummyNameAttr(entity)); 633 addPassedArg(PassEntityBy::BaseAddress, entity, characteristics); 634 } 635 } 636 637 void handleImplicitDummy( 638 const DummyCharacteristics *characteristics, 639 const Fortran::evaluate::characteristics::DummyProcedure &proc, 640 const FortranEntity &entity) { 641 TODO(interface.converter.getCurrentLocation(), 642 "handleImlicitDummy DummyProcedure"); 643 } 644 645 void 646 addFirOperand(mlir::Type type, int entityPosition, Property p, 647 llvm::ArrayRef<mlir::NamedAttribute> attributes = llvm::None) { 648 interface.inputs.emplace_back( 649 FirPlaceHolder{type, entityPosition, p, attributes}); 650 } 651 void 652 addFirResult(mlir::Type type, int entityPosition, Property p, 653 llvm::ArrayRef<mlir::NamedAttribute> attributes = llvm::None) { 654 interface.outputs.emplace_back( 655 FirPlaceHolder{type, entityPosition, p, attributes}); 656 } 657 void addPassedArg(PassEntityBy p, FortranEntity entity, 658 const DummyCharacteristics *characteristics) { 659 interface.passedArguments.emplace_back( 660 PassedEntity{p, entity, {}, {}, characteristics}); 661 } 662 int nextPassedArgPosition() { return interface.passedArguments.size(); } 663 664 Fortran::lower::AbstractConverter &getConverter() { 665 return interface.converter; 666 } 667 CallInterface &interface; 668 mlir::MLIRContext &mlirContext; 669 }; 670 671 template <typename T> 672 bool Fortran::lower::CallInterface<T>::PassedEntity::isOptional() const { 673 if (!characteristics) 674 return false; 675 return characteristics->IsOptional(); 676 } 677 template <typename T> 678 bool Fortran::lower::CallInterface<T>::PassedEntity::mayBeModifiedByCall() 679 const { 680 if (!characteristics) 681 return true; 682 return characteristics->GetIntent() != Fortran::common::Intent::In; 683 } 684 template <typename T> 685 bool Fortran::lower::CallInterface<T>::PassedEntity::mayBeReadByCall() const { 686 if (!characteristics) 687 return true; 688 return characteristics->GetIntent() != Fortran::common::Intent::Out; 689 } 690 691 template <typename T> 692 void Fortran::lower::CallInterface<T>::determineInterface( 693 bool isImplicit, 694 const Fortran::evaluate::characteristics::Procedure &procedure) { 695 CallInterfaceImpl<T> impl(*this); 696 if (isImplicit) 697 impl.buildImplicitInterface(procedure); 698 else 699 impl.buildExplicitInterface(procedure); 700 } 701 702 template <typename T> 703 mlir::FunctionType Fortran::lower::CallInterface<T>::genFunctionType() { 704 llvm::SmallVector<mlir::Type> returnTys; 705 llvm::SmallVector<mlir::Type> inputTys; 706 for (const FirPlaceHolder &placeHolder : outputs) 707 returnTys.emplace_back(placeHolder.type); 708 for (const FirPlaceHolder &placeHolder : inputs) 709 inputTys.emplace_back(placeHolder.type); 710 return mlir::FunctionType::get(&converter.getMLIRContext(), inputTys, 711 returnTys); 712 } 713 714 template class Fortran::lower::CallInterface<Fortran::lower::CalleeInterface>; 715 template class Fortran::lower::CallInterface<Fortran::lower::CallerInterface>; 716