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