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