1 //===-- Lower/CallInterface.h -- Procedure call interface ------*- C++ -*-===// 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 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ 10 // 11 //===----------------------------------------------------------------------===// 12 // 13 // Utility that defines fir call interface for procedure both on caller and 14 // and callee side and get the related FuncOp. 15 // It does not emit any FIR code but for the created mlir::func::FuncOp, instead 16 // it provides back a container of Symbol (callee side)/ActualArgument (caller 17 // side) with additional information for each element describing how it must be 18 // plugged with the mlir::func::FuncOp. 19 // It handles the fact that hidden arguments may be inserted for the result. 20 // while lowering. 21 // 22 // This utility uses the characteristic of Fortran procedures to operate, which 23 // is a term and concept used in Fortran to refer to the signature of a function 24 // or subroutine. 25 //===----------------------------------------------------------------------===// 26 27 #ifndef FORTRAN_LOWER_CALLINTERFACE_H 28 #define FORTRAN_LOWER_CALLINTERFACE_H 29 30 #include "flang/Common/reference.h" 31 #include "flang/Evaluate/characteristics.h" 32 #include "mlir/Dialect/Func/IR/FuncOps.h" 33 #include "mlir/IR/BuiltinOps.h" 34 #include <memory> 35 #include <optional> 36 37 namespace Fortran::semantics { 38 class Symbol; 39 } 40 41 namespace mlir { 42 class Location; 43 } 44 45 namespace fir { 46 class FortranProcedureFlagsEnumAttr; 47 } 48 49 namespace Fortran::lower { 50 class AbstractConverter; 51 class SymMap; 52 class HostAssociations; 53 namespace pft { 54 struct FunctionLikeUnit; 55 } 56 57 /// PassedEntityTypes helps abstract whether CallInterface is mapping a 58 /// Symbol to mlir::Value (callee side) or an ActualArgument to a position 59 /// inside the input vector for the CallOp (caller side. It will be up to the 60 /// CallInterface user to produce the mlir::Value that will go in this input 61 /// vector). 62 class CallerInterface; 63 class CalleeInterface; 64 template <typename T> 65 struct PassedEntityTypes {}; 66 template <> 67 struct PassedEntityTypes<CallerInterface> { 68 using FortranEntity = const Fortran::evaluate::ActualArgument *; 69 using FirValue = int; 70 }; 71 template <> 72 struct PassedEntityTypes<CalleeInterface> { 73 using FortranEntity = 74 std::optional<common::Reference<const semantics::Symbol>>; 75 using FirValue = mlir::Value; 76 }; 77 78 /// Implementation helper 79 template <typename T> 80 class CallInterfaceImpl; 81 82 /// CallInterface defines all the logic to determine FIR function interfaces 83 /// from a characteristic, build the mlir::func::FuncOp and describe back the 84 /// argument mapping to its user. 85 /// The logic is shared between the callee and caller sides that it accepts as 86 /// a curiously recursive template to handle the few things that cannot be 87 /// shared between both sides (getting characteristics, mangled name, location). 88 /// It maps FIR arguments to front-end Symbol (callee side) or ActualArgument 89 /// (caller side) with the same code using the abstract FortranEntity type that 90 /// can be either a Symbol or an ActualArgument. 91 /// It works in two passes: a first pass over the characteristics that decides 92 /// how the interface must be. Then, the funcOp is created for it. Then a simple 93 /// pass over fir arguments finalize the interface information that must be 94 /// passed back to the user (and may require having the funcOp). All this 95 /// passes are driven from the CallInterface constructor. 96 template <typename T> 97 class CallInterface { 98 friend CallInterfaceImpl<T>; 99 100 public: 101 /// Enum the different ways an entity can be passed-by 102 enum class PassEntityBy { 103 BaseAddress, 104 BoxChar, 105 // passing a read-only descriptor 106 Box, 107 // passing a writable descriptor 108 MutableBox, 109 AddressAndLength, 110 /// Value means passed by value at the mlir level, it is not necessarily 111 /// implied by Fortran Value attribute. 112 Value, 113 /// ValueAttribute means dummy has the Fortran VALUE attribute. 114 BaseAddressValueAttribute, 115 CharBoxValueAttribute, // BoxChar with VALUE 116 // Passing a character procedure as a <procedure address, result length> 117 // tuple. 118 CharProcTuple, 119 BoxProcRef 120 }; 121 /// Different properties of an entity that can be passed/returned. 122 /// One-to-One mapping with PassEntityBy but for 123 /// PassEntityBy::AddressAndLength that has two properties. 124 enum class Property { 125 BaseAddress, 126 BoxChar, 127 CharAddress, 128 CharLength, 129 CharProcTuple, 130 Box, 131 MutableBox, 132 Value, 133 BoxProcRef 134 }; 135 136 using FortranEntity = typename PassedEntityTypes<T>::FortranEntity; 137 using FirValue = typename PassedEntityTypes<T>::FirValue; 138 139 /// FirPlaceHolder are place holders for the mlir inputs and outputs that are 140 /// created during the first pass before the mlir::func::FuncOp is created. 141 struct FirPlaceHolder { 142 FirPlaceHolder(mlir::Type t, int passedPosition, Property p, 143 llvm::ArrayRef<mlir::NamedAttribute> attrs) 144 : type{t}, passedEntityPosition{passedPosition}, property{p}, 145 attributes{attrs} {} 146 /// Type for this input/output 147 mlir::Type type; 148 /// Position of related passedEntity in passedArguments. 149 /// (passedEntity is the passedResult this value is resultEntityPosition. 150 int passedEntityPosition; 151 static constexpr int resultEntityPosition = -1; 152 /// Indicate property of the entity passedEntityPosition that must be passed 153 /// through this argument. 154 Property property; 155 /// MLIR attributes for this argument 156 llvm::SmallVector<mlir::NamedAttribute> attributes; 157 }; 158 159 /// PassedEntity is what is provided back to the CallInterface user. 160 /// It describe how the entity is plugged in the interface 161 struct PassedEntity { 162 /// Is the dummy argument optional? 163 bool isOptional() const; 164 /// Can the argument be modified by the callee? 165 bool mayBeModifiedByCall() const; 166 /// Can the argument be read by the callee? 167 bool mayBeReadByCall() const; 168 /// Does the argument have the specified IgnoreTKR flag? 169 bool testTKR(Fortran::common::IgnoreTKR flag) const; 170 /// Is the argument INTENT(OUT) 171 bool isIntentOut() const; 172 /// Does the argument have the CONTIGUOUS attribute or have explicit shape? 173 bool mustBeMadeContiguous() const; 174 /// Does the dummy argument have the VALUE attribute? 175 bool hasValueAttribute() const; 176 /// Does the dummy argument have the ALLOCATABLE attribute? 177 bool hasAllocatableAttribute() const; 178 /// May the dummy argument require INTENT(OUT) finalization 179 /// on entry to the invoked procedure? Provides conservative answer. 180 bool mayRequireIntentoutFinalization() const; 181 /// Is the dummy argument an explicit-shape or assumed-size array that 182 /// must be passed by descriptor? Sequence association imply the actual 183 /// argument shape/rank may differ with the dummy shape/rank (see F'2023 184 /// section 15.5.2.12), so care is needed when creating the descriptor 185 /// for the dummy argument. 186 bool isSequenceAssociatedDescriptor() const; 187 /// How entity is passed by. 188 PassEntityBy passBy; 189 /// What is the entity (SymbolRef for callee/ActualArgument* for caller) 190 /// What is the related mlir::func::FuncOp argument(s) (mlir::Value for 191 /// callee / index for the caller). 192 FortranEntity entity; 193 FirValue firArgument; 194 FirValue firLength; /* only for AddressAndLength */ 195 196 /// Pointer to the argument characteristics. Nullptr for results. 197 const Fortran::evaluate::characteristics::DummyArgument *characteristics = 198 nullptr; 199 }; 200 201 /// Return the mlir::func::FuncOp. Note that front block is added by this 202 /// utility if callee side. 203 mlir::func::FuncOp getFuncOp() const { return func; } 204 /// Number of MLIR inputs/outputs of the created FuncOp. 205 std::size_t getNumFIRArguments() const { return inputs.size(); } 206 std::size_t getNumFIRResults() const { return outputs.size(); } 207 /// Return the MLIR output types. 208 llvm::SmallVector<mlir::Type> getResultType() const; 209 210 /// Return a container of Symbol/ActualArgument* and how they must 211 /// be plugged with the mlir::func::FuncOp. 212 llvm::ArrayRef<PassedEntity> getPassedArguments() const { 213 return passedArguments; 214 } 215 /// In case the result must be passed by the caller, indicate how. 216 /// nullopt if the result is not passed by the caller. 217 std::optional<PassedEntity> getPassedResult() const { return passedResult; } 218 /// Returns the mlir function type 219 mlir::FunctionType genFunctionType(); 220 221 /// determineInterface is the entry point of the first pass that defines the 222 /// interface and is required to get the mlir::func::FuncOp. 223 void 224 determineInterface(bool isImplicit, 225 const Fortran::evaluate::characteristics::Procedure &); 226 227 /// Does the caller need to allocate storage for the result ? 228 bool callerAllocateResult() const { 229 return mustPassResult() || mustSaveResult(); 230 } 231 232 /// Is the Fortran result passed as an extra MLIR argument ? 233 bool mustPassResult() const { return passedResult.has_value(); } 234 /// Must the MLIR result be saved with a fir.save_result ? 235 bool mustSaveResult() const { return saveResult; } 236 237 /// Can the associated procedure be called via an implicit interface? 238 bool canBeCalledViaImplicitInterface() const { 239 return characteristic && characteristic->CanBeCalledViaImplicitInterface(); 240 } 241 242 /// Translate Fortran procedure attributes into FIR attribute. 243 /// Return attribute is nullptr if the procedure has no attributes. 244 fir::FortranProcedureFlagsEnumAttr 245 getProcedureAttrs(mlir::MLIRContext *) const; 246 247 protected: 248 CallInterface(Fortran::lower::AbstractConverter &c) : converter{c} {} 249 /// CRTP handle. 250 T &side() { return *static_cast<T *>(this); } 251 const T &side() const { return *static_cast<const T *>(this); } 252 /// Entry point to be called by child ctor to analyze the signature and 253 /// create/find the mlir::func::FuncOp. Child needs to be initialized first. 254 void declare(); 255 /// Second pass entry point, once the mlir::func::FuncOp is created. 256 /// Nothing is done if it was already called. 257 void mapPassedEntities(); 258 void mapBackInputToPassedEntity(const FirPlaceHolder &, FirValue); 259 260 llvm::SmallVector<FirPlaceHolder> outputs; 261 llvm::SmallVector<FirPlaceHolder> inputs; 262 mlir::func::FuncOp func; 263 llvm::SmallVector<PassedEntity> passedArguments; 264 std::optional<PassedEntity> passedResult; 265 bool saveResult = false; 266 267 Fortran::lower::AbstractConverter &converter; 268 /// Store characteristic once created, it is required for further information 269 /// (e.g. getting the length of character result) 270 std::optional<Fortran::evaluate::characteristics::Procedure> characteristic = 271 std::nullopt; 272 }; 273 274 //===----------------------------------------------------------------------===// 275 // Caller side interface 276 //===----------------------------------------------------------------------===// 277 278 /// The CallerInterface provides the helpers needed by CallInterface 279 /// (getting the characteristic...) and a safe way for the user to 280 /// place the mlir::Value arguments into the input vector 281 /// once they are lowered. 282 class CallerInterface : public CallInterface<CallerInterface> { 283 public: 284 CallerInterface(const Fortran::evaluate::ProcedureRef &p, 285 Fortran::lower::AbstractConverter &c) 286 : CallInterface{c}, procRef{p} { 287 declare(); 288 mapPassedEntities(); 289 actualInputs.resize(getNumFIRArguments()); 290 } 291 292 /// CRTP callbacks 293 bool hasAlternateReturns() const; 294 std::string getMangledName() const; 295 mlir::Location getCalleeLocation() const; 296 Fortran::evaluate::characteristics::Procedure characterize() const; 297 298 const Fortran::evaluate::ProcedureRef &getCallDescription() const { 299 return procRef; 300 } 301 302 /// Get the SubprogramDetails that defines the interface of this call if it is 303 /// known at the call site. Return nullptr if it is not known. 304 const Fortran::semantics::SubprogramDetails *getInterfaceDetails() const; 305 306 bool isMainProgram() const { return false; } 307 308 /// Returns true if this is a call to a procedure pointer of a dummy 309 /// procedure. 310 bool isIndirectCall() const; 311 312 /// Returns true if this is a call of a type-bound procedure with a 313 /// polymorphic entity. 314 bool requireDispatchCall() const; 315 316 /// Get the passed-object argument index. nullopt if there is no passed-object 317 /// index. 318 std::optional<unsigned> getPassArgIndex() const; 319 320 /// Get the passed-object if any. Crashes if there is a passed object 321 /// but it was not placed in the inputs yet. Return a null value 322 /// otherwise. 323 mlir::Value getIfPassedArg() const; 324 325 /// Return the procedure symbol if this is a call to a user defined 326 /// procedure. 327 const Fortran::semantics::Symbol *getProcedureSymbol() const; 328 329 /// Return the dummy argument symbol if this is a call to a user 330 /// defined procedure with explicit interface. Returns nullptr if there 331 /// is no user defined explicit interface. 332 const Fortran::semantics::Symbol * 333 getDummySymbol(const PassedEntity &entity) const; 334 335 /// Helpers to place the lowered arguments at the right place once they 336 /// have been lowered. 337 void placeInput(const PassedEntity &passedEntity, mlir::Value arg); 338 void placeAddressAndLengthInput(const PassedEntity &passedEntity, 339 mlir::Value addr, mlir::Value len); 340 341 /// Get lowered FIR argument given the Fortran argument. 342 mlir::Value getInput(const PassedEntity &passedEntity); 343 344 /// If this is a call to a procedure pointer or dummy, returns the related 345 /// procedure designator. Nullptr otherwise. 346 const Fortran::evaluate::ProcedureDesignator *getIfIndirectCall() const; 347 348 /// Get the input vector once it is complete. 349 llvm::ArrayRef<mlir::Value> getInputs() const { 350 if (!verifyActualInputs()) 351 llvm::report_fatal_error("lowered arguments are incomplete"); 352 return actualInputs; 353 } 354 355 /// Does the caller must map function interface symbols in order to evaluate 356 /// the result specification expressions (extents and lengths) ? If needed, 357 /// this mapping must be done after argument lowering, and before the call 358 /// itself. 359 bool mustMapInterfaceSymbolsForResult() const; 360 /// Must the caller map function interface symbols in order to evaluate 361 /// the specification expressions of a given dummy argument? 362 bool mustMapInterfaceSymbolsForDummyArgument(const PassedEntity &) const; 363 364 /// Visitor for specification expression. Boolean indicate the specification 365 /// expression is for the last extent of an assumed size array. 366 using ExprVisitor = 367 std::function<void(evaluate::Expr<evaluate::SomeType>, bool)>; 368 369 /// Walk the result non-deferred extent specification expressions. 370 void walkResultExtents(const ExprVisitor &) const; 371 372 /// Walk the result non-deferred length specification expressions. 373 void walkResultLengths(const ExprVisitor &) const; 374 /// Walk non-deferred extent specification expressions of a dummy argument. 375 void walkDummyArgumentExtents(const PassedEntity &, 376 const ExprVisitor &) const; 377 /// Walk non-deferred length specification expressions of a dummy argument. 378 void walkDummyArgumentLengths(const PassedEntity &, 379 const ExprVisitor &) const; 380 381 /// Get the mlir::Value that is passed as argument \p sym of the function 382 /// being called. The arguments must have been placed before calling this 383 /// function. 384 mlir::Value getArgumentValue(const semantics::Symbol &sym) const; 385 386 /// Returns the symbol for the result in the explicit interface. If this is 387 /// called on an intrinsic or function without explicit interface, this will 388 /// crash. 389 const Fortran::semantics::Symbol &getResultSymbol() const; 390 391 /// If some storage needs to be allocated for the result, 392 /// returns the storage type. 393 mlir::Type getResultStorageType() const; 394 395 /// Return FIR type of argument. 396 mlir::Type getDummyArgumentType(const PassedEntity &) const; 397 398 // Copy of base implementation. 399 static constexpr bool hasHostAssociated() { return false; } 400 mlir::Type getHostAssociatedTy() const { 401 llvm_unreachable("getting host associated type in CallerInterface"); 402 } 403 404 private: 405 /// Check that the input vector is complete. 406 bool verifyActualInputs() const; 407 const Fortran::evaluate::ProcedureRef &procRef; 408 llvm::SmallVector<mlir::Value> actualInputs; 409 }; 410 411 //===----------------------------------------------------------------------===// 412 // Callee side interface 413 //===----------------------------------------------------------------------===// 414 415 /// CalleeInterface only provides the helpers needed by CallInterface 416 /// to abstract the specificities of the callee side. 417 class CalleeInterface : public CallInterface<CalleeInterface> { 418 public: 419 CalleeInterface(Fortran::lower::pft::FunctionLikeUnit &f, 420 Fortran::lower::AbstractConverter &c) 421 : CallInterface{c}, funit{f} { 422 declare(); 423 } 424 425 bool hasAlternateReturns() const; 426 std::string getMangledName() const; 427 mlir::Location getCalleeLocation() const; 428 Fortran::evaluate::characteristics::Procedure characterize() const; 429 bool isMainProgram() const; 430 431 Fortran::lower::pft::FunctionLikeUnit &getCallDescription() const { 432 return funit; 433 } 434 435 /// On the callee side it does not matter whether the procedure is 436 /// called through pointers or not. 437 bool isIndirectCall() const { return false; } 438 439 /// On the callee side it does not matter whether the procedure is called 440 /// through dynamic dispatch or not. 441 bool requireDispatchCall() const { return false; }; 442 443 /// Return the procedure symbol if this is a call to a user defined 444 /// procedure. 445 const Fortran::semantics::Symbol *getProcedureSymbol() const; 446 447 /// Add mlir::func::FuncOp entry block and map fir block arguments to Fortran 448 /// dummy argument symbols. 449 mlir::func::FuncOp addEntryBlockAndMapArguments(); 450 451 bool hasHostAssociated() const; 452 mlir::Type getHostAssociatedTy() const; 453 mlir::Value getHostAssociatedTuple() const; 454 455 private: 456 Fortran::lower::pft::FunctionLikeUnit &funit; 457 }; 458 459 /// Translate a procedure characteristics to an mlir::FunctionType signature. 460 mlir::FunctionType 461 translateSignature(const Fortran::evaluate::ProcedureDesignator &, 462 Fortran::lower::AbstractConverter &); 463 464 /// Declare or find the mlir::func::FuncOp for the procedure designator 465 /// \p proc. If the mlir::func::FuncOp does not exist yet, declare it with 466 /// the signature translated from the ProcedureDesignator argument. 467 /// Due to Fortran implicit function typing rules, the returned FuncOp is not 468 /// guaranteed to have the signature from ProcedureDesignator if the FuncOp was 469 /// already declared. 470 mlir::func::FuncOp 471 getOrDeclareFunction(const Fortran::evaluate::ProcedureDesignator &, 472 Fortran::lower::AbstractConverter &); 473 474 /// Return the type of an argument that is a dummy procedure. This may be an 475 /// mlir::FunctionType, but it can also be a more elaborate type based on the 476 /// function type (like a tuple<function type, length type> for character 477 /// functions). 478 mlir::Type getDummyProcedureType(const Fortran::semantics::Symbol &dummyProc, 479 Fortran::lower::AbstractConverter &); 480 481 /// Return !fir.boxproc<() -> ()> type. 482 mlir::Type getUntypedBoxProcType(mlir::MLIRContext *context); 483 484 /// Return true if \p ty is "!fir.ref<i64>", which is the interface for 485 /// type(C_PTR/C_FUNPTR) passed by value. 486 bool isCPtrArgByValueType(mlir::Type ty); 487 488 /// Is it required to pass \p proc as a tuple<function address, result length> ? 489 // This is required to convey the length of character functions passed as dummy 490 // procedures. 491 bool mustPassLengthWithDummyProcedure( 492 const Fortran::evaluate::ProcedureDesignator &proc, 493 Fortran::lower::AbstractConverter &); 494 495 } // namespace Fortran::lower 496 497 #endif // FORTRAN_LOWER_FIRBUILDER_H 498