Skip to content

Commit c373f58

Browse files
authored
[flang] Lower procedure pointer components (#75453)
Lower procedure pointer components, except in the context of structure constructor (left TODO). Procedure pointer components lowering share most of the lowering logic of procedure poionters with the following particularities: - They are components, so an hlfir.designate must be generated to retrieve the procedure pointer address from its derived type base. - They may have a PASS argument. While there is no dispatching as with type bound procedure, special care must be taken to retrieve the derived type component base in this case since semantics placed it in the argument list and not in the evaluate::ProcedureDesignator. These components also bring a new level of recursive MLIR types since a fir.type may now contain a component with an MLIR function type where one of the argument is the fir.type itself. This required moving the "derived type in construction" stackto the converter so that the object and function type lowering utilities share the same state (currently the function type utilty would end-up creating a new stack when lowering its arguments, leading to infinite loops). The BoxedProcedurePass also needed an update to deal with this recursive aspect.
1 parent 747061f commit c373f58

19 files changed

+441
-59
lines changed

flang/include/flang/Lower/AbstractConverter.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ struct Variable;
5858

5959
using SomeExpr = Fortran::evaluate::Expr<Fortran::evaluate::SomeType>;
6060
using SymbolRef = Fortran::common::Reference<const Fortran::semantics::Symbol>;
61+
using TypeConstructionStack =
62+
llvm::SmallVector<std::pair<const Fortran::lower::SymbolRef, mlir::Type>>;
6163
class StatementContext;
6264

6365
using ExprToValueMap = llvm::DenseMap<const SomeExpr *, mlir::Value>;
@@ -231,6 +233,10 @@ class AbstractConverter {
231233
const Fortran::semantics::DerivedTypeSpec &typeSpec,
232234
fir::RecordType type) = 0;
233235

236+
/// Get stack of derived type in construction. This is an internal entry point
237+
/// for the type conversion utility to allow lowering recursive derived types.
238+
virtual TypeConstructionStack &getTypeConstructionStack() = 0;
239+
234240
//===--------------------------------------------------------------------===//
235241
// Locations
236242
//===--------------------------------------------------------------------===//

flang/include/flang/Lower/CallInterface.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,11 @@ class CallerInterface : public CallInterface<CallerInterface> {
303303
/// index.
304304
std::optional<unsigned> getPassArgIndex() const;
305305

306+
/// Get the passed-object if any. Crashes if there is a passed object
307+
/// but it was not placed in the inputs yet. Return a null value
308+
/// otherwise.
309+
mlir::Value getIfPassedArg() const;
310+
306311
/// Return the procedure symbol if this is a call to a user defined
307312
/// procedure.
308313
const Fortran::semantics::Symbol *getProcedureSymbol() const;
@@ -314,8 +319,8 @@ class CallerInterface : public CallInterface<CallerInterface> {
314319
mlir::Value addr, mlir::Value len);
315320

316321
/// If this is a call to a procedure pointer or dummy, returns the related
317-
/// symbol. Nullptr otherwise.
318-
const Fortran::semantics::Symbol *getIfIndirectCallSymbol() const;
322+
/// procedure designator. Nullptr otherwise.
323+
const Fortran::evaluate::ProcedureDesignator *getIfIndirectCall() const;
319324

320325
/// Get the input vector once it is complete.
321326
llvm::ArrayRef<mlir::Value> getInputs() const {

flang/include/flang/Lower/ConvertProcedureDesignator.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,13 @@ mlir::Value
6060
convertProcedureDesignatorInitialTarget(Fortran::lower::AbstractConverter &,
6161
mlir::Location,
6262
const Fortran::semantics::Symbol &sym);
63+
64+
/// Given the value of a "PASS" actual argument \p passedArg and the
65+
/// evaluate::ProcedureDesignator for the call, address and dereference
66+
/// the argument's procedure pointer component that must be called.
67+
mlir::Value derefPassProcPointerComponent(
68+
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
69+
const Fortran::evaluate::ProcedureDesignator &proc, mlir::Value passedArg,
70+
Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx);
6371
} // namespace Fortran::lower
6472
#endif // FORTRAN_LOWER_CONVERT_PROCEDURE_DESIGNATOR_H

flang/include/flang/Optimizer/Support/InternalNames.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,14 @@ struct NameUniquer {
156156
static std::string
157157
getTypeDescriptorBindingTableName(llvm::StringRef mangledTypeName);
158158

159+
/// Remove markers that have been added when doing partial type
160+
/// conversions. mlir::Type cannot be mutated in a pass, so new
161+
/// fir::RecordType must be created when lowering member types.
162+
/// Suffixes added to these new types are meaningless and are
163+
/// dropped in the names passed to LLVM.
164+
static llvm::StringRef
165+
dropTypeConversionMarkers(llvm::StringRef mangledTypeName);
166+
159167
private:
160168
static std::string intAsString(std::int64_t i);
161169
static std::string doKind(std::int64_t kind);

flang/lib/Lower/Bridge.cpp

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -170,25 +170,22 @@ class TypeInfoConverter {
170170
if (seen.contains(typeInfoSym))
171171
return;
172172
seen.insert(typeInfoSym);
173-
if (!skipRegistration) {
174-
registeredTypeInfo.emplace_back(
175-
TypeInfo{typeInfoSym, typeSpec, type, loc});
176-
return;
177-
}
178-
// Once the registration is closed, symbols cannot be added to the
179-
// registeredTypeInfoSymbols list because it may be iterated over.
180-
// However, after registration is closed, it is safe to directly generate
181-
// the globals because all FuncOps whose addresses may be required by the
182-
// initializers have been generated.
183-
createTypeInfoOpAndGlobal(converter,
184-
TypeInfo{typeInfoSym, typeSpec, type, loc});
173+
currentTypeInfoStack->emplace_back(
174+
TypeInfo{typeInfoSym, typeSpec, type, loc});
175+
return;
185176
}
186177

187178
void createTypeInfo(Fortran::lower::AbstractConverter &converter) {
188-
skipRegistration = true;
189-
for (const TypeInfo &info : registeredTypeInfo)
190-
createTypeInfoOpAndGlobal(converter, info);
191-
registeredTypeInfo.clear();
179+
while (!registeredTypeInfoA.empty()) {
180+
currentTypeInfoStack = &registeredTypeInfoB;
181+
for (const TypeInfo &info : registeredTypeInfoA)
182+
createTypeInfoOpAndGlobal(converter, info);
183+
registeredTypeInfoA.clear();
184+
currentTypeInfoStack = &registeredTypeInfoA;
185+
for (const TypeInfo &info : registeredTypeInfoB)
186+
createTypeInfoOpAndGlobal(converter, info);
187+
registeredTypeInfoB.clear();
188+
}
192189
}
193190

194191
private:
@@ -249,11 +246,12 @@ class TypeInfoConverter {
249246
}
250247

251248
/// Store the front-end data that will be required to generate the type info
252-
/// for the derived types that have been converted to fir.type<>.
253-
llvm::SmallVector<TypeInfo> registeredTypeInfo;
254-
/// Create derived type info immediately without storing the
255-
/// symbol in registeredTypeInfo.
256-
bool skipRegistration = false;
249+
/// for the derived types that have been converted to fir.type<>. There are
250+
/// two stacks since the type info may visit new types, so the new types must
251+
/// be added to a new stack.
252+
llvm::SmallVector<TypeInfo> registeredTypeInfoA;
253+
llvm::SmallVector<TypeInfo> registeredTypeInfoB;
254+
llvm::SmallVector<TypeInfo> *currentTypeInfoStack = &registeredTypeInfoA;
257255
/// Track symbols symbols processed during and after the registration
258256
/// to avoid infinite loops between type conversions and global variable
259257
/// creation.
@@ -602,6 +600,11 @@ class FirConverter : public Fortran::lower::AbstractConverter {
602600
std::nullopt);
603601
}
604602

603+
Fortran::lower::TypeConstructionStack &
604+
getTypeConstructionStack() override final {
605+
return typeConstructionStack;
606+
}
607+
605608
bool isPresentShallowLookup(Fortran::semantics::Symbol &sym) override final {
606609
return bool(shallowLookupSymbol(sym));
607610
}
@@ -5008,6 +5011,13 @@ class FirConverter : public Fortran::lower::AbstractConverter {
50085011
bool ompDeviceCodeFound = false;
50095012

50105013
const Fortran::lower::ExprToValueMap *exprValueOverrides{nullptr};
5014+
5015+
/// Stack of derived type under construction to avoid infinite loops when
5016+
/// dealing with recursive derived types. This is held in the bridge because
5017+
/// the state needs to be maintained between data and function type lowering
5018+
/// utilities to deal with procedure pointer components whose arguments have
5019+
/// the type of the containing derived type.
5020+
Fortran::lower::TypeConstructionStack typeConstructionStack;
50115021
};
50125022

50135023
} // namespace

flang/lib/Lower/CallInterface.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ bool Fortran::lower::CallerInterface::isIndirectCall() const {
8787
}
8888

8989
bool Fortran::lower::CallerInterface::requireDispatchCall() const {
90+
// Procedure pointer component reference do not require dispatch, but
91+
// have PASS/NOPASS argument.
92+
if (const Fortran::semantics::Symbol *sym = procRef.proc().GetSymbol())
93+
if (Fortran::semantics::IsPointer(*sym))
94+
return false;
9095
// calls with NOPASS attribute still have their component so check if it is
9196
// polymorphic.
9297
if (const Fortran::evaluate::Component *component =
@@ -127,12 +132,21 @@ Fortran::lower::CallerInterface::getPassArgIndex() const {
127132
return passArg;
128133
}
129134

130-
const Fortran::semantics::Symbol *
131-
Fortran::lower::CallerInterface::getIfIndirectCallSymbol() const {
135+
mlir::Value Fortran::lower::CallerInterface::getIfPassedArg() const {
136+
if (std::optional<unsigned> passArg = getPassArgIndex()) {
137+
assert(actualInputs.size() > *passArg && actualInputs[*passArg] &&
138+
"passed arg was not set yet");
139+
return actualInputs[*passArg];
140+
}
141+
return {};
142+
}
143+
144+
const Fortran::evaluate::ProcedureDesignator *
145+
Fortran::lower::CallerInterface::getIfIndirectCall() const {
132146
if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
133147
if (Fortran::semantics::IsPointer(*symbol) ||
134148
Fortran::semantics::IsDummy(*symbol))
135-
return symbol;
149+
return &procRef.proc();
136150
return nullptr;
137151
}
138152

flang/lib/Lower/ConvertCall.cpp

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "flang/Lower/ConvertCall.h"
1414
#include "flang/Lower/Allocatable.h"
1515
#include "flang/Lower/ConvertExprToHLFIR.h"
16+
#include "flang/Lower/ConvertProcedureDesignator.h"
1617
#include "flang/Lower/ConvertVariable.h"
1718
#include "flang/Lower/CustomIntrinsicCall.h"
1819
#include "flang/Lower/HlfirIntrinsics.h"
@@ -165,20 +166,28 @@ fir::ExtendedValue Fortran::lower::genCallOpAndResult(
165166
// will be used only if there is no explicit length in the local interface).
166167
mlir::Value funcPointer;
167168
mlir::Value charFuncPointerLength;
168-
if (const Fortran::semantics::Symbol *sym =
169-
caller.getIfIndirectCallSymbol()) {
170-
funcPointer = fir::getBase(converter.getSymbolExtendedValue(*sym, &symMap));
171-
if (!funcPointer)
172-
fir::emitFatalError(loc, "failed to find indirect call symbol address");
173-
if (fir::isCharacterProcedureTuple(funcPointer.getType(),
174-
/*acceptRawFunc=*/false))
175-
std::tie(funcPointer, charFuncPointerLength) =
176-
fir::factory::extractCharacterProcedureTuple(builder, loc,
177-
funcPointer);
178-
// Reference to a procedure pointer. Load its value, the address of the
179-
// procedure it points to.
180-
if (Fortran::semantics::IsProcedurePointer(sym))
181-
funcPointer = builder.create<fir::LoadOp>(loc, funcPointer);
169+
if (const Fortran::evaluate::ProcedureDesignator *procDesignator =
170+
caller.getIfIndirectCall()) {
171+
if (mlir::Value passedArg = caller.getIfPassedArg()) {
172+
// Procedure pointer component call with PASS argument. To avoid
173+
// "double" lowering of the ComponentRef, semantics only place the
174+
// ComponentRef in the ActualArguments, not in the ProcedureDesignator (
175+
// that is only the component symbol).
176+
// Fetch the passed argument and addresses of its procedure pointer
177+
// component.
178+
funcPointer = Fortran::lower::derefPassProcPointerComponent(
179+
loc, converter, *procDesignator, passedArg, symMap, stmtCtx);
180+
} else {
181+
Fortran::lower::SomeExpr expr{*procDesignator};
182+
fir::ExtendedValue loweredProc =
183+
converter.genExprAddr(loc, expr, stmtCtx);
184+
funcPointer = fir::getBase(loweredProc);
185+
// Dummy procedure may have assumed length, in which case the result
186+
// length was passed along the dummy procedure.
187+
// This is not possible with procedure pointer components.
188+
if (const fir::CharBoxValue *charBox = loweredProc.getCharBox())
189+
charFuncPointerLength = charBox->getLen();
190+
}
182191
}
183192

184193
mlir::IndexType idxTy = builder.getIndexType();

flang/lib/Lower/ConvertConstant.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,8 @@ static mlir::Value genStructureComponentInit(
366366
TODO(loc, "allocatable component in structure constructor");
367367

368368
if (Fortran::semantics::IsPointer(sym)) {
369+
if (Fortran::semantics::IsProcedure(sym))
370+
TODO(loc, "procedure pointer component initial value");
369371
mlir::Value initialTarget =
370372
Fortran::lower::genInitialDataTarget(converter, loc, componentTy, expr);
371373
res = builder.create<fir::InsertValueOp>(

flang/lib/Lower/ConvertExpr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4849,7 +4849,7 @@ class ArrayExprLowering {
48494849
}
48504850
}
48514851

4852-
if (caller.getIfIndirectCallSymbol())
4852+
if (caller.getIfIndirectCall())
48534853
fir::emitFatalError(loc, "cannot be indirect call");
48544854

48554855
// The lambda is mutable so that `caller` copy can be modified inside it.

flang/lib/Lower/ConvertExprToHLFIR.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1738,6 +1738,8 @@ class HlfirBuilder {
17381738

17391739
if (attrs && bitEnumContainsAny(attrs.getFlags(),
17401740
fir::FortranVariableFlagsEnum::pointer)) {
1741+
if (Fortran::semantics::IsProcedure(sym))
1742+
TODO(loc, "procedure pointer component in structure constructor");
17411743
// Pointer component construction is just a copy of the box contents.
17421744
fir::ExtendedValue lhsExv =
17431745
hlfir::translateToExtendedValue(loc, builder, lhs);

flang/lib/Lower/ConvertProcedureDesignator.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "flang/Optimizer/Builder/IntrinsicCall.h"
2020
#include "flang/Optimizer/Builder/Todo.h"
2121
#include "flang/Optimizer/Dialect/FIROps.h"
22+
#include "flang/Optimizer/HLFIR/HLFIROps.h"
2223

2324
static bool areAllSymbolsInExprMapped(const Fortran::evaluate::ExtentExpr &expr,
2425
Fortran::lower::SymMap &symMap) {
@@ -96,6 +97,49 @@ fir::ExtendedValue Fortran::lower::convertProcedureDesignator(
9697
return funcPtr;
9798
}
9899

100+
static hlfir::EntityWithAttributes designateProcedurePointerComponent(
101+
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
102+
const Fortran::evaluate::Symbol &procComponentSym, mlir::Value base,
103+
Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
104+
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
105+
fir::FortranVariableFlagsAttr attributes =
106+
Fortran::lower::translateSymbolAttributes(builder.getContext(),
107+
procComponentSym);
108+
/// Passed argument may be a descriptor. This is a scalar reference, so the
109+
/// base address can be directly addressed.
110+
if (base.getType().isa<fir::BaseBoxType>())
111+
base = builder.create<fir::BoxAddrOp>(loc, base);
112+
std::string fieldName = converter.getRecordTypeFieldName(procComponentSym);
113+
auto recordType =
114+
hlfir::getFortranElementType(base.getType()).cast<fir::RecordType>();
115+
mlir::Type fieldType = recordType.getType(fieldName);
116+
// FIXME: semantics is not expanding intermediate parent components in:
117+
// call x%p() where p is a component of a parent type of x type.
118+
if (!fieldType)
119+
TODO(loc, "reference to procedure pointer component from parent type");
120+
mlir::Type designatorType = fir::ReferenceType::get(fieldType);
121+
mlir::Value compRef = builder.create<hlfir::DesignateOp>(
122+
loc, designatorType, base, fieldName,
123+
/*compShape=*/mlir::Value{}, hlfir::DesignateOp::Subscripts{},
124+
/*substring=*/mlir::ValueRange{},
125+
/*complexPart=*/std::nullopt,
126+
/*shape=*/mlir::Value{}, /*typeParams=*/mlir::ValueRange{}, attributes);
127+
return hlfir::EntityWithAttributes{compRef};
128+
}
129+
130+
static hlfir::EntityWithAttributes convertProcedurePointerComponent(
131+
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
132+
const Fortran::evaluate::Component &procComponent,
133+
Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
134+
fir::ExtendedValue baseExv = Fortran::lower::convertDataRefToValue(
135+
loc, converter, procComponent.base(), symMap, stmtCtx);
136+
mlir::Value base = fir::getBase(baseExv);
137+
const Fortran::semantics::Symbol &procComponentSym =
138+
procComponent.GetLastSymbol();
139+
return designateProcedurePointerComponent(loc, converter, procComponentSym,
140+
base, symMap, stmtCtx);
141+
}
142+
99143
hlfir::EntityWithAttributes Fortran::lower::convertProcedureDesignatorToHLFIR(
100144
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
101145
const Fortran::evaluate::ProcedureDesignator &proc,
@@ -109,6 +153,10 @@ hlfir::EntityWithAttributes Fortran::lower::convertProcedureDesignatorToHLFIR(
109153
return *varDef;
110154
}
111155

156+
if (const Fortran::evaluate::Component *procComponent = proc.GetComponent())
157+
return convertProcedurePointerComponent(loc, converter, *procComponent,
158+
symMap, stmtCtx);
159+
112160
fir::ExtendedValue procExv =
113161
convertProcedureDesignator(loc, converter, proc, symMap, stmtCtx);
114162
// Directly package the procedure address as a fir.boxproc or
@@ -148,3 +196,15 @@ mlir::Value Fortran::lower::convertProcedureDesignatorInitialTarget(
148196
return fir::getBase(Fortran::lower::convertToAddress(
149197
loc, converter, procVal, stmtCtx, procVal.getType()));
150198
}
199+
200+
mlir::Value Fortran::lower::derefPassProcPointerComponent(
201+
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
202+
const Fortran::evaluate::ProcedureDesignator &proc, mlir::Value passedArg,
203+
Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
204+
const Fortran::semantics::Symbol *procComponentSym = proc.GetSymbol();
205+
assert(procComponentSym &&
206+
"failed to retrieve pointer procedure component symbol");
207+
hlfir::EntityWithAttributes pointerComp = designateProcedurePointerComponent(
208+
loc, converter, *procComponentSym, passedArg, symMap, stmtCtx);
209+
return converter.getFirOpBuilder().create<fir::LoadOp>(loc, pointerComp);
210+
}

flang/lib/Lower/ConvertType.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ namespace {
140140
struct TypeBuilderImpl {
141141

142142
TypeBuilderImpl(Fortran::lower::AbstractConverter &converter)
143-
: converter{converter}, context{&converter.getMLIRContext()} {}
143+
: derivedTypeInConstruction{converter.getTypeConstructionStack()},
144+
converter{converter}, context{&converter.getMLIRContext()} {}
144145

145146
template <typename A>
146147
mlir::Type genExprType(const A &expr) {
@@ -398,8 +399,6 @@ struct TypeBuilderImpl {
398399
assert(scopeIter != derivedScope.cend() &&
399400
"failed to find derived type component symbol");
400401
const Fortran::semantics::Symbol &component = scopeIter->second.get();
401-
if (IsProcedure(component))
402-
TODO(converter.genLocation(component.name()), "procedure components");
403402
mlir::Type ty = genSymbolType(component);
404403
cs.emplace_back(converter.getRecordTypeFieldName(component), ty);
405404
}
@@ -568,8 +567,7 @@ struct TypeBuilderImpl {
568567
/// Stack derived type being processed to avoid infinite loops in case of
569568
/// recursive derived types. The depth of derived types is expected to be
570569
/// shallow (<10), so a SmallVector is sufficient.
571-
llvm::SmallVector<std::pair<const Fortran::lower::SymbolRef, mlir::Type>>
572-
derivedTypeInConstruction;
570+
Fortran::lower::TypeConstructionStack &derivedTypeInConstruction;
573571
Fortran::lower::AbstractConverter &converter;
574572
mlir::MLIRContext *context;
575573
};

0 commit comments

Comments
 (0)