Skip to content

[Flang]: Lowering reference to functions that return a procedure pointer #78194

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 33 additions & 32 deletions flang/lib/Lower/CallInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ bool Fortran::lower::CallerInterface::mustMapInterfaceSymbols() const {
const std::optional<Fortran::evaluate::characteristics::FunctionResult>
&result = characteristic->functionResult;
if (!result || result->CanBeReturnedViaImplicitInterface() ||
!getInterfaceDetails())
!getInterfaceDetails() || result->IsProcedurePointer())
return false;
bool allResultSpecExprConstant = true;
auto visitor = [&](const Fortran::lower::SomeExpr &e) {
Expand Down Expand Up @@ -1111,39 +1111,40 @@ class Fortran::lower::CallInterfaceImpl {
const Fortran::evaluate::characteristics::FunctionResult &result) {
using Attr = Fortran::evaluate::characteristics::FunctionResult::Attr;
mlir::Type mlirType;
if (auto proc{result.IsProcedurePointer()})
if (auto proc{result.IsProcedurePointer()}) {
mlirType = fir::BoxProcType::get(
&mlirContext, getProcedureType(*proc, interface.converter));
else {
const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
result.GetTypeAndShape();
assert(typeAndShape && "expect type for non proc pointer result");
mlirType = translateDynamicType(typeAndShape->type());
const auto *resTypeAndShape{result.GetTypeAndShape()};
bool resIsPolymorphic =
resTypeAndShape && resTypeAndShape->type().IsPolymorphic();
bool resIsAssumedType =
resTypeAndShape && resTypeAndShape->type().IsAssumedType();
if (std::optional<fir::SequenceType::Shape> bounds =
getBounds(*typeAndShape))
mlirType = fir::SequenceType::get(*bounds, mlirType);
if (result.attrs.test(Attr::Allocatable))
mlirType = fir::wrapInClassOrBoxType(
fir::HeapType::get(mlirType), resIsPolymorphic, resIsAssumedType);
if (result.attrs.test(Attr::Pointer))
mlirType =
fir::wrapInClassOrBoxType(fir::PointerType::get(mlirType),
resIsPolymorphic, resIsAssumedType);

if (fir::isa_char(mlirType)) {
// Character scalar results must be passed as arguments in lowering so
// that an assumed length character function callee can access the
// result length. A function with a result requiring an explicit
// interface does not have to be compatible with assumed length
// function, but most compilers supports it.
handleImplicitCharacterResult(typeAndShape->type());
return;
}
addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
Property::Value);
return;
}
const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
result.GetTypeAndShape();
assert(typeAndShape && "expect type for non proc pointer result");
mlirType = translateDynamicType(typeAndShape->type());
const auto *resTypeAndShape{result.GetTypeAndShape()};
bool resIsPolymorphic =
resTypeAndShape && resTypeAndShape->type().IsPolymorphic();
bool resIsAssumedType =
resTypeAndShape && resTypeAndShape->type().IsAssumedType();
if (std::optional<fir::SequenceType::Shape> bounds =
getBounds(*typeAndShape))
mlirType = fir::SequenceType::get(*bounds, mlirType);
if (result.attrs.test(Attr::Allocatable))
mlirType = fir::wrapInClassOrBoxType(fir::HeapType::get(mlirType),
resIsPolymorphic, resIsAssumedType);
if (result.attrs.test(Attr::Pointer))
mlirType = fir::wrapInClassOrBoxType(fir::PointerType::get(mlirType),
resIsPolymorphic, resIsAssumedType);

if (fir::isa_char(mlirType)) {
// Character scalar results must be passed as arguments in lowering so
// that an assumed length character function callee can access the
// result length. A function with a result requiring an explicit
// interface does not have to be compatible with assumed length
// function, but most compilers supports it.
handleImplicitCharacterResult(typeAndShape->type());
return;
}

addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
Expand Down
3 changes: 3 additions & 0 deletions flang/lib/Lower/ConvertCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1379,6 +1379,9 @@ genUserCall(Fortran::lower::PreparedActualArguments &loweredActuals,
loc, callContext.converter, callContext.symMap, callContext.stmtCtx,
caller, callSiteType, callContext.resultType,
callContext.isElementalProcWithArrayArgs());
// For procedure pointer function result, just return the call.
if (callContext.resultType && callContext.resultType->isa<fir::BoxProcType>())
return hlfir::EntityWithAttributes(fir::getBase(result));

/// Clean-up associations and copy-in.
for (auto cleanUp : callCleanUps)
Expand Down
10 changes: 7 additions & 3 deletions flang/lib/Lower/ConvertExprToHLFIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1433,9 +1433,13 @@ class HlfirBuilder {
}

hlfir::EntityWithAttributes gen(const Fortran::evaluate::ProcedureRef &expr) {
TODO(
getLoc(),
"lowering function references that return procedure pointers to HLFIR");
Fortran::evaluate::ProcedureDesignator proc{expr.proc()};
auto procTy{Fortran::lower::translateSignature(proc, getConverter())};
auto result = Fortran::lower::convertCallToHLFIR(getLoc(), getConverter(),
expr, procTy.getResult(0),
getSymMap(), getStmtCtx());
assert(result.has_value());
return *result;
}

template <typename T>
Expand Down
8 changes: 6 additions & 2 deletions flang/lib/Optimizer/Builder/IntrinsicCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2158,9 +2158,13 @@ fir::ExtendedValue
IntrinsicLibrary::genAssociated(mlir::Type resultType,
llvm::ArrayRef<fir::ExtendedValue> args) {
assert(args.size() == 2);
if (fir::isBoxProcAddressType(fir::getBase(args[0]).getType())) {
mlir::Type ptrTy = fir::getBase(args[0]).getType();
if (ptrTy &&
(fir::isBoxProcAddressType(ptrTy) || ptrTy.isa<fir::BoxProcType>())) {
mlir::Value pointerBoxProc =
builder.create<fir::LoadOp>(loc, fir::getBase(args[0]));
fir::isBoxProcAddressType(ptrTy)
? builder.create<fir::LoadOp>(loc, fir::getBase(args[0]))
: fir::getBase(args[0]);
mlir::Value pointerTarget =
builder.create<fir::BoxAddrOp>(loc, pointerBoxProc);
if (isStaticallyAbsent(args[1]))
Expand Down
62 changes: 62 additions & 0 deletions flang/test/Lower/HLFIR/procedure-pointer.f90
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
! 1. declaration and initialization
! 2. pointer assignment and invocation
! 3. procedure pointer argument passing.
! 3. procedure pointer function result.
! RUN: bbc -emit-hlfir -o - %s | FileCheck %s

module m
Expand Down Expand Up @@ -244,6 +245,67 @@ subroutine sub9()
! CHECK: fir.call @_QPfoo2(%[[VAL_10]]) fastmath<contract> : (!fir.ref<!fir.boxproc<() -> ()>>) -> ()
end

subroutine sub10()
use m

procedure(real_func), pointer :: p1
! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.boxproc<(!fir.ref<f32>) -> f32> {bindc_name = "p1", uniq_name = "_QFsub10Ep1"}
! CHECK: %[[VAL_1:.*]] = fir.zero_bits (!fir.ref<f32>) -> f32
! CHECK: %[[VAL_2:.*]] = fir.emboxproc %[[VAL_1]] : ((!fir.ref<f32>) -> f32) -> !fir.boxproc<(!fir.ref<f32>) -> f32>
! CHECK: fir.store %[[VAL_2]] to %[[VAL_0]] : !fir.ref<!fir.boxproc<(!fir.ref<f32>) -> f32>>
! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_0]] {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFsub10Ep1"} : (!fir.ref<!fir.boxproc<(!fir.ref<f32>) -> f32>>) -> (!fir.ref<!fir.boxproc<(!fir.ref<f32>) -> f32>>, !fir.ref<!fir.boxproc<(!fir.ref<f32>) -> f32>>)

p1 => reffunc(5)
! CHECK: %c5_i32 = arith.constant 5 : i32
! CHECK: %[[VAL_4:.*]]:3 = hlfir.associate %c5_i32 {adapt.valuebyref} : (i32) -> (!fir.ref<i32>, !fir.ref<i32>, i1)
! CHECK: %[[VAL_5:.*]] = fir.call @_QFsub10Preffunc(%[[VAL_4]]#1) fastmath<contract> : (!fir.ref<i32>) -> !fir.boxproc<(!fir.ref<f32>) -> f32>
! CHECK: fir.store %[[VAL_5]] to %[[VAL_3]]#0 : !fir.ref<!fir.boxproc<(!fir.ref<f32>) -> f32>>

contains
function reffunc(arg) result(pp)
integer :: arg
procedure(real_func), pointer :: pp
! CHECK: %[[VAL_0:.*]]:2 = hlfir.declare %arg0 {uniq_name = "_QFsub10FreffuncEarg"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
! CHECK: %[[VAL_1:.*]] = fir.alloca !fir.boxproc<(!fir.ref<f32>) -> f32> {bindc_name = "pp", uniq_name = "_QFsub10FreffuncEpp"}
! CHECK: %[[VAL_2:.*]] = fir.zero_bits (!fir.ref<f32>) -> f32
! CHECK: %[[VAL_3:.*]] = fir.emboxproc %[[VAL_2]] : ((!fir.ref<f32>) -> f32) -> !fir.boxproc<(!fir.ref<f32>) -> f32>
! CHECK: fir.store %[[VAL_3]] to %[[VAL_1]] : !fir.ref<!fir.boxproc<(!fir.ref<f32>) -> f32>>
! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[VAL_1]] {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFsub10FreffuncEpp"} : (!fir.ref<!fir.boxproc<(!fir.ref<f32>) -> f32>>) -> (!fir.ref<!fir.boxproc<(!fir.ref<f32>) -> f32>>, !fir.ref<!fir.boxproc<(!fir.ref<f32>) -> f32>>)

pp => real_func
! CHECK: %[[VAL_5:.*]] = fir.address_of(@_QPreal_func) : (!fir.ref<f32>) -> f32
! CHECK: %[[VAL_6:.*]] = fir.emboxproc %[[VAL_5]] : ((!fir.ref<f32>) -> f32) -> !fir.boxproc<() -> ()>
! CHECK: %[[VAL_7:.*]] = fir.convert %[[VAL_6]] : (!fir.boxproc<() -> ()>) -> !fir.boxproc<(!fir.ref<f32>) -> f32>
! CHECK: fir.store %[[VAL_7]] to %[[VAL_4]]#0 : !fir.ref<!fir.boxproc<(!fir.ref<f32>) -> f32>>
! CHECK: %[[VAL_8:.*]] = fir.load %[[VAL_4]]#1 : !fir.ref<!fir.boxproc<(!fir.ref<f32>) -> f32>>
! CHECK: return %[[VAL_8]] : !fir.boxproc<(!fir.ref<f32>) -> f32>
end
end

subroutine sub11()
use m
interface
function reffunc(arg) result(pp)
import
integer :: arg
procedure(char_func), pointer :: pp
end
end interface

procedure(char_func), pointer :: p1
! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.boxproc<(!fir.ref<i32>) -> !fir.box<!fir.ptr<!fir.char<1,?>>>> {bindc_name = "p1", uniq_name = "_QFsub11Ep1"}
! CHECK: %[[VAL_1:.*]] = fir.zero_bits (!fir.ref<i32>) -> !fir.box<!fir.ptr<!fir.char<1,?>>>
! CHECK: %[[VAL_2:.*]] = fir.emboxproc %[[VAL_1]] : ((!fir.ref<i32>) -> !fir.box<!fir.ptr<!fir.char<1,?>>>) -> !fir.boxproc<(!fir.ref<i32>) -> !fir.box<!fir.ptr<!fir.char<1,?>>>>
! CHECK: fir.store %[[VAL_2]] to %[[VAL_0]] : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> !fir.box<!fir.ptr<!fir.char<1,?>>>>>
! CHECK: %[[VAL_3:.*]]:2 = hlfir.declare %[[VAL_0]] {fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFsub11Ep1"} : (!fir.ref<!fir.boxproc<(!fir.ref<i32>) -> !fir.box<!fir.ptr<!fir.char<1,?>>>>>) -> (!fir.ref<!fir.boxproc<(!fir.ref<i32>) -> !fir.box<!fir.ptr<!fir.char<1,?>>>>>, !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> !fir.box<!fir.ptr<!fir.char<1,?>>>>>)

p1 => reffunc(5)
! CHECK: %c5_i32 = arith.constant 5 : i32
! CHECK: %[[VAL_4:.*]]:3 = hlfir.associate %c5_i32 {adapt.valuebyref} : (i32) -> (!fir.ref<i32>, !fir.ref<i32>, i1)
! CHECK: %[[VAL_5:.*]] = fir.call @_QPreffunc(%4#1) fastmath<contract> : (!fir.ref<i32>) -> !fir.boxproc<(!fir.ref<i32>) -> !fir.box<!fir.ptr<!fir.char<1,?>>>>
! CHECK: fir.store %[[VAL_5]] to %[[VAL_3]]#0 : !fir.ref<!fir.boxproc<(!fir.ref<i32>) -> !fir.box<!fir.ptr<!fir.char<1,?>>>>>
! CHECK: return
end

! CHECK-LABEL: fir.global internal @_QFsub1Ep2 : !fir.boxproc<(!fir.ref<f32>) -> f32> {
! CHECK: %[[VAL_0:.*]] = fir.zero_bits (!fir.ref<f32>) -> f32
Expand Down
28 changes: 28 additions & 0 deletions flang/test/Lower/Intrinsics/associated-proc-pointers.f90
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,31 @@ character(10) function char_func()
! CHECK: %[[VAL_18:.*]] = arith.cmpi ne, %[[VAL_17]], %[[VAL_14]] : i64
! CHECK: %[[VAL_19:.*]] = arith.andi %[[VAL_16]], %[[VAL_18]] : i1
! CHECK: %[[VAL_20:.*]] = fir.convert %[[VAL_19]] : (i1) -> !fir.logical<4>

subroutine test_proc_pointer_6()
interface
real function func()
end
end interface
logical :: ll
ll = associated(reffunc(), func)
contains
function reffunc() result(pp)
procedure(func), pointer :: pp
end
end
! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.logical<4> {bindc_name = "ll", uniq_name = "_QFtest_proc_pointer_6Ell"}
! CHECK: %[[VAL_1:.*]]:2 = hlfir.declare %[[VAL_0]] {uniq_name = "_QFtest_proc_pointer_6Ell"} : (!fir.ref<!fir.logical<4>>) -> (!fir.ref<!fir.logical<4>>, !fir.ref<!fir.logical<4>>)
! CHECK: %[[VAL_2:.*]] = fir.call @_QFtest_proc_pointer_6Preffunc() fastmath<contract> : () -> !fir.boxproc<() -> f32>
! CHECK: %[[VAL_3:.*]] = fir.address_of(@_QPfunc) : () -> f32
! CHECK: %[[VAL_4:.*]] = fir.emboxproc %[[VAL_3]] : (() -> f32) -> !fir.boxproc<() -> ()>
! CHECK: %[[VAL_5:.*]] = fir.box_addr %[[VAL_2]] : (!fir.boxproc<() -> f32>) -> (() -> f32)
! CHECK: %[[VAL_6:.*]] = fir.box_addr %[[VAL_4]] : (!fir.boxproc<() -> ()>) -> (() -> ())
! CHECK: %[[VAL_7:.*]] = fir.convert %[[VAL_5]] : (() -> f32) -> i64
! CHECK: %[[VAL_8:.*]] = fir.convert %[[VAL_6]] : (() -> ()) -> i64
! CHECK: %[[VAL_9:.*]] = arith.cmpi eq, %[[VAL_7]], %[[VAL_8]] : i64
! CHECK: %c0_i64 = arith.constant 0 : i64
! CHECK: %[[VAL_10:.*]] = arith.cmpi ne, %c0_i64, %[[VAL_7]] : i64
! CHECK: %[[VAL_11:.*]] = arith.andi %[[VAL_9]], %[[VAL_10]] : i1
! CHECK: %[[VAL_12:.*]] = fir.convert %[[VAL_11]] : (i1) -> !fir.logical<4>
! CHECK: hlfir.assign %[[VAL_12]] to %[[VAL_1]]#0 : !fir.logical<4>, !fir.ref<!fir.logical<4>>