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 bool isBindC = procedure.IsBindC(); 512 // Handle arguments 513 const auto &argumentEntities = 514 getEntityContainer(interface.side().getCallDescription()); 515 for (auto pair : llvm::zip(procedure.dummyArguments, argumentEntities)) { 516 const Fortran::evaluate::characteristics::DummyArgument 517 &argCharacteristics = std::get<0>(pair); 518 std::visit( 519 Fortran::common::visitors{ 520 [&](const Fortran::evaluate::characteristics::DummyDataObject 521 &dummy) { 522 const auto &entity = getDataObjectEntity(std::get<1>(pair)); 523 if (dummy.CanBePassedViaImplicitInterface()) 524 handleImplicitDummy(&argCharacteristics, dummy, entity); 525 else 526 handleExplicitDummy(&argCharacteristics, dummy, entity, 527 isBindC); 528 }, 529 [&](const Fortran::evaluate::characteristics::DummyProcedure 530 &dummy) { 531 const auto &entity = getDataObjectEntity(std::get<1>(pair)); 532 handleImplicitDummy(&argCharacteristics, dummy, entity); 533 }, 534 [&](const Fortran::evaluate::characteristics::AlternateReturn &) { 535 // nothing to do 536 }, 537 }, 538 argCharacteristics.u); 539 } 540 } 541 542 private: 543 void handleImplicitResult( 544 const Fortran::evaluate::characteristics::FunctionResult &result) { 545 if (result.IsProcedurePointer()) 546 TODO(interface.converter.getCurrentLocation(), 547 "procedure pointer result not yet handled"); 548 const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape = 549 result.GetTypeAndShape(); 550 assert(typeAndShape && "expect type for non proc pointer result"); 551 Fortran::evaluate::DynamicType dynamicType = typeAndShape->type(); 552 if (dynamicType.category() == Fortran::common::TypeCategory::Character) { 553 TODO(interface.converter.getCurrentLocation(), 554 "implicit result character type"); 555 } else if (dynamicType.category() == 556 Fortran::common::TypeCategory::Derived) { 557 TODO(interface.converter.getCurrentLocation(), 558 "implicit result derived type"); 559 } else { 560 // All result other than characters/derived are simply returned by value 561 // in implicit interfaces 562 mlir::Type mlirType = 563 getConverter().genType(dynamicType.category(), dynamicType.kind()); 564 addFirResult(mlirType, FirPlaceHolder::resultEntityPosition, 565 Property::Value); 566 } 567 } 568 569 void handleExplicitResult( 570 const Fortran::evaluate::characteristics::FunctionResult &result) { 571 using Attr = Fortran::evaluate::characteristics::FunctionResult::Attr; 572 573 if (result.IsProcedurePointer()) 574 TODO(interface.converter.getCurrentLocation(), 575 "procedure pointer results"); 576 const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape = 577 result.GetTypeAndShape(); 578 assert(typeAndShape && "expect type for non proc pointer result"); 579 Fortran::evaluate::DynamicType dynamicType = typeAndShape->type(); 580 if (dynamicType.category() == Fortran::common::TypeCategory::Character) { 581 TODO(interface.converter.getCurrentLocation(), 582 "implicit result character type"); 583 } else if (dynamicType.category() == 584 Fortran::common::TypeCategory::Derived) { 585 TODO(interface.converter.getCurrentLocation(), 586 "implicit result derived type"); 587 } 588 mlir::Type mlirType = 589 getConverter().genType(dynamicType.category(), dynamicType.kind()); 590 fir::SequenceType::Shape bounds = getBounds(typeAndShape->shape()); 591 if (!bounds.empty()) 592 mlirType = fir::SequenceType::get(bounds, mlirType); 593 if (result.attrs.test(Attr::Allocatable)) 594 mlirType = fir::BoxType::get(fir::HeapType::get(mlirType)); 595 if (result.attrs.test(Attr::Pointer)) 596 mlirType = fir::BoxType::get(fir::PointerType::get(mlirType)); 597 598 addFirResult(mlirType, FirPlaceHolder::resultEntityPosition, 599 Property::Value); 600 } 601 602 fir::SequenceType::Shape getBounds(const Fortran::evaluate::Shape &shape) { 603 fir::SequenceType::Shape bounds; 604 for (const std::optional<Fortran::evaluate::ExtentExpr> &extent : shape) { 605 fir::SequenceType::Extent bound = fir::SequenceType::getUnknownExtent(); 606 if (std::optional<std::int64_t> i = toInt64(extent)) 607 bound = *i; 608 bounds.emplace_back(bound); 609 } 610 return bounds; 611 } 612 std::optional<std::int64_t> 613 toInt64(std::optional< 614 Fortran::evaluate::Expr<Fortran::evaluate::SubscriptInteger>> 615 expr) { 616 if (expr) 617 return Fortran::evaluate::ToInt64(Fortran::evaluate::Fold( 618 getConverter().getFoldingContext(), toEvExpr(*expr))); 619 return std::nullopt; 620 } 621 622 /// Return a vector with an attribute with the name of the argument if this 623 /// is a callee interface and the name is available. Otherwise, just return 624 /// an empty vector. 625 llvm::SmallVector<mlir::NamedAttribute> 626 dummyNameAttr(const FortranEntity &entity) { 627 if constexpr (std::is_same_v<FortranEntity, 628 std::optional<Fortran::common::Reference< 629 const Fortran::semantics::Symbol>>>) { 630 if (entity.has_value()) { 631 const Fortran::semantics::Symbol *argument = &*entity.value(); 632 // "fir.bindc_name" is used for arguments for the sake of consistency 633 // with other attributes carrying surface syntax names in FIR. 634 return {mlir::NamedAttribute( 635 mlir::StringAttr::get(&mlirContext, "fir.bindc_name"), 636 mlir::StringAttr::get(&mlirContext, 637 toStringRef(argument->name())))}; 638 } 639 } 640 return {}; 641 } 642 643 // Define when an explicit argument must be passed in a fir.box. 644 bool dummyRequiresBox( 645 const Fortran::evaluate::characteristics::DummyDataObject &obj) { 646 using ShapeAttr = Fortran::evaluate::characteristics::TypeAndShape::Attr; 647 using ShapeAttrs = Fortran::evaluate::characteristics::TypeAndShape::Attrs; 648 constexpr ShapeAttrs shapeRequiringBox = { 649 ShapeAttr::AssumedShape, ShapeAttr::DeferredShape, 650 ShapeAttr::AssumedRank, ShapeAttr::Coarray}; 651 if ((obj.type.attrs() & shapeRequiringBox).any()) 652 // Need to pass shape/coshape info in fir.box. 653 return true; 654 if (obj.type.type().IsPolymorphic()) 655 // Need to pass dynamic type info in fir.box. 656 return true; 657 if (const Fortran::semantics::DerivedTypeSpec *derived = 658 Fortran::evaluate::GetDerivedTypeSpec(obj.type.type())) 659 // Need to pass type parameters in fir.box if any. 660 return derived->parameters().empty(); 661 return false; 662 } 663 664 mlir::Type 665 translateDynamicType(const Fortran::evaluate::DynamicType &dynamicType) { 666 Fortran::common::TypeCategory cat = dynamicType.category(); 667 // DERIVED 668 if (cat == Fortran::common::TypeCategory::Derived) { 669 TODO(interface.converter.getCurrentLocation(), 670 "[translateDynamicType] Derived"); 671 } 672 // CHARACTER with compile time constant length. 673 if (cat == Fortran::common::TypeCategory::Character) 674 TODO(interface.converter.getCurrentLocation(), 675 "[translateDynamicType] Character"); 676 // INTEGER, REAL, LOGICAL, COMPLEX, and CHARACTER with dynamic length. 677 return getConverter().genType(cat, dynamicType.kind()); 678 } 679 680 void handleExplicitDummy( 681 const DummyCharacteristics *characteristics, 682 const Fortran::evaluate::characteristics::DummyDataObject &obj, 683 const FortranEntity &entity, bool isBindC) { 684 using Attrs = Fortran::evaluate::characteristics::DummyDataObject::Attr; 685 686 bool isValueAttr = false; 687 [[maybe_unused]] mlir::Location loc = 688 interface.converter.getCurrentLocation(); 689 llvm::SmallVector<mlir::NamedAttribute> attrs = dummyNameAttr(entity); 690 auto addMLIRAttr = [&](llvm::StringRef attr) { 691 attrs.emplace_back(mlir::StringAttr::get(&mlirContext, attr), 692 mlir::UnitAttr::get(&mlirContext)); 693 }; 694 if (obj.attrs.test(Attrs::Optional)) 695 addMLIRAttr(fir::getOptionalAttrName()); 696 if (obj.attrs.test(Attrs::Asynchronous)) 697 TODO(loc, "Asynchronous in procedure interface"); 698 if (obj.attrs.test(Attrs::Contiguous)) 699 addMLIRAttr(fir::getContiguousAttrName()); 700 if (obj.attrs.test(Attrs::Value)) 701 isValueAttr = true; // TODO: do we want an mlir::Attribute as well? 702 if (obj.attrs.test(Attrs::Volatile)) 703 TODO(loc, "Volatile in procedure interface"); 704 if (obj.attrs.test(Attrs::Target)) 705 addMLIRAttr(fir::getTargetAttrName()); 706 707 // TODO: intents that require special care (e.g finalization) 708 709 using ShapeAttr = Fortran::evaluate::characteristics::TypeAndShape::Attr; 710 const Fortran::evaluate::characteristics::TypeAndShape::Attrs &shapeAttrs = 711 obj.type.attrs(); 712 if (shapeAttrs.test(ShapeAttr::AssumedRank)) 713 TODO(loc, "Assumed Rank in procedure interface"); 714 if (shapeAttrs.test(ShapeAttr::Coarray)) 715 TODO(loc, "Coarray in procedure interface"); 716 717 // So far assume that if the argument cannot be passed by implicit interface 718 // it must be by box. That may no be always true (e.g for simple optionals) 719 720 Fortran::evaluate::DynamicType dynamicType = obj.type.type(); 721 mlir::Type type = translateDynamicType(dynamicType); 722 fir::SequenceType::Shape bounds = getBounds(obj.type.shape()); 723 if (!bounds.empty()) 724 type = fir::SequenceType::get(bounds, type); 725 if (obj.attrs.test(Attrs::Allocatable)) 726 type = fir::HeapType::get(type); 727 if (obj.attrs.test(Attrs::Pointer)) 728 type = fir::PointerType::get(type); 729 mlir::Type boxType = fir::BoxType::get(type); 730 731 if (obj.attrs.test(Attrs::Allocatable) || obj.attrs.test(Attrs::Pointer)) { 732 // Pass as fir.ref<fir.box> 733 mlir::Type boxRefType = fir::ReferenceType::get(boxType); 734 addFirOperand(boxRefType, nextPassedArgPosition(), Property::MutableBox, 735 attrs); 736 addPassedArg(PassEntityBy::MutableBox, entity, characteristics); 737 } else if (dummyRequiresBox(obj)) { 738 // Pass as fir.box 739 addFirOperand(boxType, nextPassedArgPosition(), Property::Box, attrs); 740 addPassedArg(PassEntityBy::Box, entity, characteristics); 741 } else if (dynamicType.category() == 742 Fortran::common::TypeCategory::Character) { 743 // Pass as fir.box_char 744 mlir::Type boxCharTy = 745 fir::BoxCharType::get(&mlirContext, dynamicType.kind()); 746 addFirOperand(boxCharTy, nextPassedArgPosition(), Property::BoxChar, 747 attrs); 748 addPassedArg(isValueAttr ? PassEntityBy::CharBoxValueAttribute 749 : PassEntityBy::BoxChar, 750 entity, characteristics); 751 } else { 752 // Pass as fir.ref unless it's by VALUE and BIND(C) 753 mlir::Type passType = fir::ReferenceType::get(type); 754 PassEntityBy passBy = PassEntityBy::BaseAddress; 755 Property prop = Property::BaseAddress; 756 if (isValueAttr) { 757 if (isBindC) { 758 passBy = PassEntityBy::Value; 759 prop = Property::Value; 760 passType = type; 761 } else { 762 passBy = PassEntityBy::BaseAddressValueAttribute; 763 } 764 } 765 addFirOperand(passType, nextPassedArgPosition(), prop, attrs); 766 addPassedArg(passBy, entity, characteristics); 767 } 768 } 769 770 void handleImplicitDummy( 771 const DummyCharacteristics *characteristics, 772 const Fortran::evaluate::characteristics::DummyDataObject &obj, 773 const FortranEntity &entity) { 774 Fortran::evaluate::DynamicType dynamicType = obj.type.type(); 775 if (dynamicType.category() == Fortran::common::TypeCategory::Character) { 776 mlir::Type boxCharTy = 777 fir::BoxCharType::get(&mlirContext, dynamicType.kind()); 778 addFirOperand(boxCharTy, nextPassedArgPosition(), Property::BoxChar, 779 dummyNameAttr(entity)); 780 addPassedArg(PassEntityBy::BoxChar, entity, characteristics); 781 } else { 782 // non-PDT derived type allowed in implicit interface. 783 Fortran::common::TypeCategory cat = dynamicType.category(); 784 mlir::Type type = getConverter().genType(cat, dynamicType.kind()); 785 fir::SequenceType::Shape bounds = getBounds(obj.type.shape()); 786 if (!bounds.empty()) 787 type = fir::SequenceType::get(bounds, type); 788 mlir::Type refType = fir::ReferenceType::get(type); 789 addFirOperand(refType, nextPassedArgPosition(), Property::BaseAddress, 790 dummyNameAttr(entity)); 791 addPassedArg(PassEntityBy::BaseAddress, entity, characteristics); 792 } 793 } 794 795 void handleImplicitDummy( 796 const DummyCharacteristics *characteristics, 797 const Fortran::evaluate::characteristics::DummyProcedure &proc, 798 const FortranEntity &entity) { 799 TODO(interface.converter.getCurrentLocation(), 800 "handleImlicitDummy DummyProcedure"); 801 } 802 803 void 804 addFirOperand(mlir::Type type, int entityPosition, Property p, 805 llvm::ArrayRef<mlir::NamedAttribute> attributes = llvm::None) { 806 interface.inputs.emplace_back( 807 FirPlaceHolder{type, entityPosition, p, attributes}); 808 } 809 void 810 addFirResult(mlir::Type type, int entityPosition, Property p, 811 llvm::ArrayRef<mlir::NamedAttribute> attributes = llvm::None) { 812 interface.outputs.emplace_back( 813 FirPlaceHolder{type, entityPosition, p, attributes}); 814 } 815 void addPassedArg(PassEntityBy p, FortranEntity entity, 816 const DummyCharacteristics *characteristics) { 817 interface.passedArguments.emplace_back( 818 PassedEntity{p, entity, {}, {}, characteristics}); 819 } 820 int nextPassedArgPosition() { return interface.passedArguments.size(); } 821 822 Fortran::lower::AbstractConverter &getConverter() { 823 return interface.converter; 824 } 825 CallInterface &interface; 826 mlir::MLIRContext &mlirContext; 827 }; 828 829 template <typename T> 830 bool Fortran::lower::CallInterface<T>::PassedEntity::isOptional() const { 831 if (!characteristics) 832 return false; 833 return characteristics->IsOptional(); 834 } 835 template <typename T> 836 bool Fortran::lower::CallInterface<T>::PassedEntity::mayBeModifiedByCall() 837 const { 838 if (!characteristics) 839 return true; 840 return characteristics->GetIntent() != Fortran::common::Intent::In; 841 } 842 template <typename T> 843 bool Fortran::lower::CallInterface<T>::PassedEntity::mayBeReadByCall() const { 844 if (!characteristics) 845 return true; 846 return characteristics->GetIntent() != Fortran::common::Intent::Out; 847 } 848 849 template <typename T> 850 void Fortran::lower::CallInterface<T>::determineInterface( 851 bool isImplicit, 852 const Fortran::evaluate::characteristics::Procedure &procedure) { 853 CallInterfaceImpl<T> impl(*this); 854 if (isImplicit) 855 impl.buildImplicitInterface(procedure); 856 else 857 impl.buildExplicitInterface(procedure); 858 } 859 860 template <typename T> 861 mlir::FunctionType Fortran::lower::CallInterface<T>::genFunctionType() { 862 llvm::SmallVector<mlir::Type> returnTys; 863 llvm::SmallVector<mlir::Type> inputTys; 864 for (const FirPlaceHolder &placeHolder : outputs) 865 returnTys.emplace_back(placeHolder.type); 866 for (const FirPlaceHolder &placeHolder : inputs) 867 inputTys.emplace_back(placeHolder.type); 868 return mlir::FunctionType::get(&converter.getMLIRContext(), inputTys, 869 returnTys); 870 } 871 872 template class Fortran::lower::CallInterface<Fortran::lower::CalleeInterface>; 873 template class Fortran::lower::CallInterface<Fortran::lower::CallerInterface>; 874