Skip to content

Commit e07fec1

Browse files
authored
[Flang] Add partial support for lowering procedure pointer assignment. (#70461)
**Scope of the PR:** 1. Lowering global and local procedure pointer declaration statement with explicit or implicit interface. The explicit interface can be from an interface block, a module procedure or an internal procedure. 2. Lowering procedure pointer assignment, where the target procedure could be external, module or internal procedures. 3. Lowering reference to procedure pointers so that it works end to end. **PR notes:** 1. The first commit of the PR does not include testing. I would like to collect some comments first, which may alter the output. Once I confirm the implementation, I will add some testing as a follow up commit to this PR. 2. No special handling of the host-associated entities when an internal procedure is the target of a procedure pointer assignment in this PR. **Implementation notes:** 1. The implementation is using the HLFIR path. 2. Flang currently uses `getUntypedBoxProcType` to get the `fir::BoxProcType` for `ProcedureDesignator` when getting the address of a procedure in order to pass it as an actual argument. This PR inherits the same design decision for procedure pointer as the `fir::StoreOp` requires the same memory type.
1 parent a842430 commit e07fec1

17 files changed

+562
-51
lines changed

flang/include/flang/Lower/BoxAnalyzer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,8 @@ class BoxAnalyzer : public fir::details::matcher<BoxAnalyzer> {
382382

383383
/// Run the analysis on `sym`.
384384
void analyze(const Fortran::semantics::Symbol &sym) {
385+
if (Fortran::semantics::IsProcedurePointer(sym))
386+
return;
385387
if (symIsArray(sym)) {
386388
bool isConstant = !isAssumedSize(sym);
387389
llvm::SmallVector<int64_t> lbounds;

flang/include/flang/Lower/CallInterface.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ class CallInterface {
111111
CharBoxValueAttribute, // BoxChar with VALUE
112112
// Passing a character procedure as a <procedure address, result length>
113113
// tuple.
114-
CharProcTuple
114+
CharProcTuple,
115+
BoxProcRef
115116
};
116117
/// Different properties of an entity that can be passed/returned.
117118
/// One-to-One mapping with PassEntityBy but for
@@ -124,7 +125,8 @@ class CallInterface {
124125
CharProcTuple,
125126
Box,
126127
MutableBox,
127-
Value
128+
Value,
129+
BoxProcRef
128130
};
129131

130132
using FortranEntity = typename PassedEntityTypes<T>::FortranEntity;

flang/include/flang/Lower/ConvertProcedureDesignator.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
namespace mlir {
2121
class Location;
22+
class Value;
23+
class Type;
2224
}
2325
namespace fir {
2426
class ExtendedValue;
@@ -29,6 +31,9 @@ class EntityWithAttributes;
2931
namespace Fortran::evaluate {
3032
struct ProcedureDesignator;
3133
}
34+
namespace Fortran::semantics {
35+
class Symbol;
36+
}
3237

3338
namespace Fortran::lower {
3439
class AbstractConverter;
@@ -50,5 +55,10 @@ hlfir::EntityWithAttributes convertProcedureDesignatorToHLFIR(
5055
const Fortran::evaluate::ProcedureDesignator &proc,
5156
Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx);
5257

58+
/// Generate initialization for procedure pointer to procedure target.
59+
mlir::Value
60+
convertProcedureDesignatorInitialTarget(Fortran::lower::AbstractConverter &,
61+
mlir::Location,
62+
const Fortran::semantics::Symbol &sym);
5363
} // namespace Fortran::lower
5464
#endif // FORTRAN_LOWER_CONVERT_PROCEDURE_DESIGNATOR_H

flang/include/flang/Optimizer/Builder/FIRBuilder.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,10 @@ mlir::Value genCPtrOrCFunptrValue(fir::FirOpBuilder &builder,
677677
/// to keep all the lower bound and explicit parameter information.
678678
fir::BoxValue createBoxValue(fir::FirOpBuilder &builder, mlir::Location loc,
679679
const fir::ExtendedValue &exv);
680+
681+
/// Generate Null BoxProc for procedure pointer null initialization.
682+
mlir::Value createNullBoxProc(fir::FirOpBuilder &builder, mlir::Location loc,
683+
mlir::Type boxType);
680684
} // namespace fir::factory
681685

682686
#endif // FORTRAN_OPTIMIZER_BUILDER_FIRBUILDER_H

flang/include/flang/Optimizer/Builder/HLFIRTools.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ class Entity : public mlir::Value {
5858
bool isValue() const { return isFortranValue(*this); }
5959
bool isVariable() const { return !isValue(); }
6060
bool isMutableBox() const { return hlfir::isBoxAddressType(getType()); }
61+
bool isProcedurePointer() const {
62+
return hlfir::isBoxProcAddressType(getType());
63+
}
6164
bool isBoxAddressOrValue() const {
6265
return hlfir::isBoxAddressOrValueType(getType());
6366
}

flang/include/flang/Optimizer/HLFIR/HLFIRDialect.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ inline bool isBoxAddressType(mlir::Type type) {
6767
return type && type.isa<fir::BaseBoxType>();
6868
}
6969

70+
/// Is this a fir.boxproc address type?
71+
inline bool isBoxProcAddressType(mlir::Type type) {
72+
type = fir::dyn_cast_ptrEleTy(type);
73+
return type && type.isa<fir::BoxProcType>();
74+
}
75+
7076
/// Is this a fir.box or fir.class address or value type?
7177
inline bool isBoxAddressOrValueType(mlir::Type type) {
7278
return fir::unwrapRefType(type).isa<fir::BaseBoxType>();

flang/lib/Lower/Bridge.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3095,6 +3095,17 @@ class FirConverter : public Fortran::lower::AbstractConverter {
30953095
const Fortran::lower::SomeExpr *expr =
30963096
Fortran::semantics::GetExpr(pointerObject);
30973097
assert(expr);
3098+
if (Fortran::evaluate::IsProcedurePointer(*expr)) {
3099+
Fortran::lower::StatementContext stmtCtx;
3100+
hlfir::Entity pptr = Fortran::lower::convertExprToHLFIR(
3101+
loc, *this, *expr, localSymbols, stmtCtx);
3102+
auto boxTy{
3103+
Fortran::lower::getUntypedBoxProcType(builder->getContext())};
3104+
hlfir::Entity nullBoxProc(
3105+
fir::factory::createNullBoxProc(*builder, loc, boxTy));
3106+
builder->createStoreWithConvert(loc, nullBoxProc, pptr);
3107+
return;
3108+
}
30983109
fir::MutableBoxValue box = genExprMutableBox(loc, *expr);
30993110
fir::factory::disassociateMutableBox(*builder, loc, box);
31003111
}
@@ -3241,8 +3252,24 @@ class FirConverter : public Fortran::lower::AbstractConverter {
32413252
mlir::Location loc, const Fortran::evaluate::Assignment &assign,
32423253
const Fortran::evaluate::Assignment::BoundsSpec &lbExprs) {
32433254
Fortran::lower::StatementContext stmtCtx;
3244-
if (Fortran::evaluate::IsProcedure(assign.rhs))
3255+
3256+
if (!lowerToHighLevelFIR() && Fortran::evaluate::IsProcedure(assign.rhs))
32453257
TODO(loc, "procedure pointer assignment");
3258+
if (Fortran::evaluate::IsProcedurePointer(assign.lhs)) {
3259+
hlfir::Entity lhs = Fortran::lower::convertExprToHLFIR(
3260+
loc, *this, assign.lhs, localSymbols, stmtCtx);
3261+
if (Fortran::evaluate::IsNullProcedurePointer(assign.rhs)) {
3262+
auto boxTy{Fortran::lower::getUntypedBoxProcType(&getMLIRContext())};
3263+
hlfir::Entity rhs(
3264+
fir::factory::createNullBoxProc(*builder, loc, boxTy));
3265+
builder->createStoreWithConvert(loc, rhs, lhs);
3266+
return;
3267+
}
3268+
hlfir::Entity rhs(getBase(Fortran::lower::convertExprToAddress(
3269+
loc, *this, assign.rhs, localSymbols, stmtCtx)));
3270+
builder->createStoreWithConvert(loc, rhs, lhs);
3271+
return;
3272+
}
32463273

32473274
std::optional<Fortran::evaluate::DynamicType> lhsType =
32483275
assign.lhs.GetType();

flang/lib/Lower/CallInterface.cpp

Lines changed: 56 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
#include "flang/Semantics/tools.h"
2424
#include <optional>
2525

26+
static mlir::FunctionType
27+
getProcedureType(const Fortran::evaluate::characteristics::Procedure &proc,
28+
Fortran::lower::AbstractConverter &converter);
29+
2630
mlir::Type Fortran::lower::getUntypedBoxProcType(mlir::MLIRContext *context) {
2731
llvm::SmallVector<mlir::Type> resultTys;
2832
llvm::SmallVector<mlir::Type> inputTys;
@@ -1055,15 +1059,24 @@ class Fortran::lower::CallInterfaceImpl {
10551059
const DummyCharacteristics *characteristics,
10561060
const Fortran::evaluate::characteristics::DummyProcedure &proc,
10571061
const FortranEntity &entity) {
1058-
if (proc.attrs.test(
1062+
if (!interface.converter.getLoweringOptions().getLowerToHighLevelFIR() &&
1063+
proc.attrs.test(
10591064
Fortran::evaluate::characteristics::DummyProcedure::Attr::Pointer))
10601065
TODO(interface.converter.getCurrentLocation(),
10611066
"procedure pointer arguments");
1062-
// Otherwise, it is a dummy procedure.
10631067
const Fortran::evaluate::characteristics::Procedure &procedure =
10641068
proc.procedure.value();
10651069
mlir::Type funcType =
10661070
getProcedureDesignatorType(&procedure, interface.converter);
1071+
if (proc.attrs.test(Fortran::evaluate::characteristics::DummyProcedure::
1072+
Attr::Pointer)) {
1073+
// Prodecure pointer dummy argument.
1074+
funcType = fir::ReferenceType::get(funcType);
1075+
addFirOperand(funcType, nextPassedArgPosition(), Property::BoxProcRef);
1076+
addPassedArg(PassEntityBy::BoxProcRef, entity, characteristics);
1077+
return;
1078+
}
1079+
// Otherwise, it is a dummy procedure.
10671080
std::optional<Fortran::evaluate::DynamicType> resultTy =
10681081
getResultDynamicType(procedure);
10691082
if (resultTy && mustPassLengthWithDummyProcedure(procedure)) {
@@ -1087,37 +1100,40 @@ class Fortran::lower::CallInterfaceImpl {
10871100
void handleExplicitResult(
10881101
const Fortran::evaluate::characteristics::FunctionResult &result) {
10891102
using Attr = Fortran::evaluate::characteristics::FunctionResult::Attr;
1090-
1091-
if (result.IsProcedurePointer())
1092-
TODO(interface.converter.getCurrentLocation(),
1093-
"procedure pointer results");
1094-
const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
1095-
result.GetTypeAndShape();
1096-
assert(typeAndShape && "expect type for non proc pointer result");
1097-
mlir::Type mlirType = translateDynamicType(typeAndShape->type());
1098-
fir::SequenceType::Shape bounds = getBounds(typeAndShape->shape());
1099-
const auto *resTypeAndShape{result.GetTypeAndShape()};
1100-
bool resIsPolymorphic =
1101-
resTypeAndShape && resTypeAndShape->type().IsPolymorphic();
1102-
bool resIsAssumedType =
1103-
resTypeAndShape && resTypeAndShape->type().IsAssumedType();
1104-
if (!bounds.empty())
1105-
mlirType = fir::SequenceType::get(bounds, mlirType);
1106-
if (result.attrs.test(Attr::Allocatable))
1107-
mlirType = fir::wrapInClassOrBoxType(fir::HeapType::get(mlirType),
1108-
resIsPolymorphic, resIsAssumedType);
1109-
if (result.attrs.test(Attr::Pointer))
1110-
mlirType = fir::wrapInClassOrBoxType(fir::PointerType::get(mlirType),
1111-
resIsPolymorphic, resIsAssumedType);
1112-
1113-
if (fir::isa_char(mlirType)) {
1114-
// Character scalar results must be passed as arguments in lowering so
1115-
// that an assumed length character function callee can access the result
1116-
// length. A function with a result requiring an explicit interface does
1117-
// not have to be compatible with assumed length function, but most
1118-
// compilers supports it.
1119-
handleImplicitCharacterResult(typeAndShape->type());
1120-
return;
1103+
mlir::Type mlirType;
1104+
if (auto proc{result.IsProcedurePointer()})
1105+
mlirType = fir::BoxProcType::get(
1106+
&mlirContext, getProcedureType(*proc, interface.converter));
1107+
else {
1108+
const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
1109+
result.GetTypeAndShape();
1110+
assert(typeAndShape && "expect type for non proc pointer result");
1111+
mlirType = translateDynamicType(typeAndShape->type());
1112+
fir::SequenceType::Shape bounds = getBounds(typeAndShape->shape());
1113+
const auto *resTypeAndShape{result.GetTypeAndShape()};
1114+
bool resIsPolymorphic =
1115+
resTypeAndShape && resTypeAndShape->type().IsPolymorphic();
1116+
bool resIsAssumedType =
1117+
resTypeAndShape && resTypeAndShape->type().IsAssumedType();
1118+
if (!bounds.empty())
1119+
mlirType = fir::SequenceType::get(bounds, mlirType);
1120+
if (result.attrs.test(Attr::Allocatable))
1121+
mlirType = fir::wrapInClassOrBoxType(
1122+
fir::HeapType::get(mlirType), resIsPolymorphic, resIsAssumedType);
1123+
if (result.attrs.test(Attr::Pointer))
1124+
mlirType =
1125+
fir::wrapInClassOrBoxType(fir::PointerType::get(mlirType),
1126+
resIsPolymorphic, resIsAssumedType);
1127+
1128+
if (fir::isa_char(mlirType)) {
1129+
// Character scalar results must be passed as arguments in lowering so
1130+
// that an assumed length character function callee can access the
1131+
// result length. A function with a result requiring an explicit
1132+
// interface does not have to be compatible with assumed length
1133+
// function, but most compilers supports it.
1134+
handleImplicitCharacterResult(typeAndShape->type());
1135+
return;
1136+
}
11211137
}
11221138

11231139
addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
@@ -1534,3 +1550,10 @@ bool Fortran::lower::isCPtrArgByValueType(mlir::Type ty) {
15341550
return ty.isa<fir::ReferenceType>() &&
15351551
fir::isa_integer(fir::unwrapRefType(ty));
15361552
}
1553+
1554+
// Return the mlir::FunctionType of a procedure
1555+
static mlir::FunctionType
1556+
getProcedureType(const Fortran::evaluate::characteristics::Procedure &proc,
1557+
Fortran::lower::AbstractConverter &converter) {
1558+
return SignatureBuilder{proc, converter, false}.genFunctionType();
1559+
}

flang/lib/Lower/ConvertCall.cpp

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ fir::ExtendedValue Fortran::lower::genCallOpAndResult(
175175
std::tie(funcPointer, charFuncPointerLength) =
176176
fir::factory::extractCharacterProcedureTuple(builder, loc,
177177
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);
178182
}
179183

180184
mlir::IndexType idxTy = builder.getIndexType();
@@ -870,9 +874,39 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
870874
// element if this is an array in an elemental call.
871875
hlfir::Entity actual = preparedActual.getActual(loc, builder);
872876

873-
// Do nothing if this is a procedure argument. It is already a
874-
// fir.boxproc/fir.tuple<fir.boxproc, len> as it should.
877+
// Handle the procedure pointer actual arguments.
878+
if (actual.isProcedurePointer()) {
879+
// Procedure pointer actual to procedure pointer dummy.
880+
if (hlfir::isBoxProcAddressType(dummyType))
881+
return PreparedDummyArgument{actual, /*cleanups=*/{}};
882+
// Procedure pointer actual to procedure dummy.
883+
if (hlfir::isFortranProcedureValue(dummyType)) {
884+
actual = hlfir::derefPointersAndAllocatables(loc, builder, actual);
885+
return PreparedDummyArgument{actual, /*cleanups=*/{}};
886+
}
887+
}
888+
889+
// NULL() actual to procedure pointer dummy
890+
if (Fortran::evaluate::IsNullProcedurePointer(expr) &&
891+
hlfir::isBoxProcAddressType(dummyType)) {
892+
auto boxTy{Fortran::lower::getUntypedBoxProcType(builder.getContext())};
893+
auto tempBoxProc{builder.createTemporary(loc, boxTy)};
894+
hlfir::Entity nullBoxProc(
895+
fir::factory::createNullBoxProc(builder, loc, boxTy));
896+
builder.create<fir::StoreOp>(loc, nullBoxProc, tempBoxProc);
897+
return PreparedDummyArgument{tempBoxProc, /*cleanups=*/{}};
898+
}
899+
875900
if (actual.isProcedure()) {
901+
// Procedure actual to procedure pointer dummy.
902+
if (hlfir::isBoxProcAddressType(dummyType)) {
903+
auto tempBoxProc{builder.createTemporary(loc, actual.getType())};
904+
builder.create<fir::StoreOp>(loc, actual, tempBoxProc);
905+
return PreparedDummyArgument{tempBoxProc, /*cleanups=*/{}};
906+
}
907+
// Procedure actual to procedure dummy.
908+
// Do nothing if this is a procedure argument. It is already a
909+
// fir.boxproc/fir.tuple<fir.boxproc, len> as it should.
876910
if (actual.getType() != dummyType)
877911
actual = fixProcedureDummyMismatch(loc, builder, actual, dummyType);
878912
return PreparedDummyArgument{actual, /*cleanups=*/{}};
@@ -1158,6 +1192,7 @@ genUserCall(Fortran::lower::PreparedActualArguments &loweredActuals,
11581192
case PassBy::CharBoxValueAttribute:
11591193
case PassBy::Box:
11601194
case PassBy::BaseAddress:
1195+
case PassBy::BoxProcRef:
11611196
case PassBy::BoxChar: {
11621197
PreparedDummyArgument preparedDummy =
11631198
prepareUserCallActualArgument(loc, builder, *preparedActual, argTy,
@@ -1174,6 +1209,8 @@ genUserCall(Fortran::lower::PreparedActualArguments &loweredActuals,
11741209
break;
11751210
case PassBy::CharProcTuple: {
11761211
hlfir::Entity actual = preparedActual->getActual(loc, builder);
1212+
if (actual.isProcedurePointer())
1213+
actual = hlfir::derefPointersAndAllocatables(loc, builder, actual);
11771214
if (!fir::isCharacterProcedureTuple(actual.getType()))
11781215
actual = fixProcedureDummyMismatch(loc, builder, actual, argTy);
11791216
caller.placeInput(arg, actual);
@@ -1495,6 +1532,8 @@ genIntrinsicRefCore(Fortran::lower::PreparedActualArguments &loweredActuals,
14951532
}
14961533

14971534
hlfir::Entity actual = arg.value()->getActual(loc, builder);
1535+
if (actual.isProcedurePointer())
1536+
TODO(loc, "Procedure pointer as actual argument to intrinsics.");
14981537
switch (argRules.lowerAs) {
14991538
case fir::LowerIntrinsicArgAs::Value:
15001539
operands.emplace_back(
@@ -2149,8 +2188,10 @@ genProcedureRef(CallContext &callContext) {
21492188
TODO(loc, "assumed type actual argument");
21502189
if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(
21512190
*expr)) {
2152-
if (arg.passBy !=
2153-
Fortran::lower::CallerInterface::PassEntityBy::MutableBox) {
2191+
if ((arg.passBy !=
2192+
Fortran::lower::CallerInterface::PassEntityBy::MutableBox) &&
2193+
(arg.passBy !=
2194+
Fortran::lower::CallerInterface::PassEntityBy::BoxProcRef)) {
21542195
assert(
21552196
arg.isOptional() &&
21562197
"NULL must be passed only to pointer, allocatable, or OPTIONAL");

flang/lib/Lower/ConvertExpr.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4845,6 +4845,9 @@ class ArrayExprLowering {
48454845
}
48464846
// See C15100 and C15101
48474847
fir::emitFatalError(loc, "cannot be POINTER, ALLOCATABLE");
4848+
case PassBy::BoxProcRef:
4849+
// Procedure pointer: no action here.
4850+
break;
48484851
}
48494852
}
48504853

flang/lib/Lower/ConvertExprToHLFIR.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1425,7 +1425,9 @@ class HlfirBuilder {
14251425
}
14261426

14271427
hlfir::EntityWithAttributes gen(const Fortran::evaluate::ProcedureRef &expr) {
1428-
TODO(getLoc(), "lowering ProcRef to HLFIR");
1428+
TODO(
1429+
getLoc(),
1430+
"lowering function references that return procedure pointers to HLFIR");
14291431
}
14301432

14311433
template <typename T>

0 commit comments

Comments
 (0)