Skip to content

Commit 67402fe

Browse files
authored
[flang] Do not move finalized function results in lowering (#80683)
Fortran requires finalizing function results when the result type have final procedures. Lowering was unconditionally "moving" function results into values "hlfir.expr". This is not correct when the results are finalized because it means the function result storage will be used after the hlfir.expr. Only move function results that are not finalized.
1 parent fff86c6 commit 67402fe

File tree

6 files changed

+82
-30
lines changed

6 files changed

+82
-30
lines changed

flang/include/flang/Lower/ConvertCall.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ namespace Fortran::lower {
3030
/// link to internal procedures.
3131
/// \p isElemental must be set to true if elemental call is being produced.
3232
/// It is only used for HLFIR.
33-
fir::ExtendedValue genCallOpAndResult(
33+
/// The returned boolean indicates if finalization has been emitted in
34+
/// \p stmtCtx for the result.
35+
std::pair<fir::ExtendedValue, bool> genCallOpAndResult(
3436
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
3537
Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
3638
Fortran::lower::CallerInterface &caller, mlir::FunctionType callSiteType,

flang/include/flang/Optimizer/Builder/MutableBox.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,8 @@ void syncMutableBoxFromIRBox(fir::FirOpBuilder &builder, mlir::Location loc,
150150
fir::ExtendedValue genMutableBoxRead(fir::FirOpBuilder &builder,
151151
mlir::Location loc,
152152
const fir::MutableBoxValue &box,
153-
bool mayBePolymorphic = true);
153+
bool mayBePolymorphic = true,
154+
bool preserveLowerBounds = true);
154155

155156
/// Returns the fir.ref<fir.box<T>> of a MutableBoxValue filled with the current
156157
/// association / allocation properties. If the fir.ref<fir.box> already exists

flang/lib/Lower/ConvertCall.cpp

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ static llvm::cl::opt<bool> useHlfirIntrinsicOps(
4242
llvm::cl::desc("Lower via HLFIR transformational intrinsic operations such "
4343
"as hlfir.sum"));
4444

45+
static constexpr char tempResultName[] = ".tmp.func_result";
46+
4547
/// Helper to package a Value and its properties into an ExtendedValue.
4648
static fir::ExtendedValue toExtendedValue(mlir::Location loc, mlir::Value base,
4749
llvm::ArrayRef<mlir::Value> extents,
@@ -147,7 +149,7 @@ static bool mustCastFuncOpToCopeWithImplicitInterfaceMismatch(
147149
return false;
148150
}
149151

150-
fir::ExtendedValue Fortran::lower::genCallOpAndResult(
152+
std::pair<fir::ExtendedValue, bool> Fortran::lower::genCallOpAndResult(
151153
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
152154
Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
153155
Fortran::lower::CallerInterface &caller, mlir::FunctionType callSiteType,
@@ -478,6 +480,7 @@ fir::ExtendedValue Fortran::lower::genCallOpAndResult(
478480
[](const auto &) {});
479481

480482
// 7.5.6.3 point 5. Derived-type finalization for nonpointer function.
483+
bool resultIsFinalized = false;
481484
// Check if the derived-type is finalizable if it is a monomorphic
482485
// derived-type.
483486
// For polymorphic and unlimited polymorphic enities call the runtime
@@ -499,6 +502,7 @@ fir::ExtendedValue Fortran::lower::genCallOpAndResult(
499502
fir::runtime::genDerivedTypeDestroy(*bldr, loc,
500503
fir::getBase(*allocatedResult));
501504
});
505+
resultIsFinalized = true;
502506
} else {
503507
const Fortran::semantics::DerivedTypeSpec &typeSpec =
504508
retTy->GetDerivedTypeSpec();
@@ -513,14 +517,17 @@ fir::ExtendedValue Fortran::lower::genCallOpAndResult(
513517
mlir::Value box = bldr->createBox(loc, *allocatedResult);
514518
fir::runtime::genDerivedTypeDestroy(*bldr, loc, box);
515519
});
520+
resultIsFinalized = true;
516521
}
517522
}
518523
}
519-
return *allocatedResult;
524+
return {*allocatedResult, resultIsFinalized};
520525
}
521526

527+
// subroutine call
522528
if (!resultType)
523-
return mlir::Value{}; // subroutine call
529+
return {fir::ExtendedValue{mlir::Value{}}, /*resultIsFinalized=*/false};
530+
524531
// For now, Fortran return values are implemented with a single MLIR
525532
// function return value.
526533
assert(callNumResults == 1 && "Expected exactly one result in FUNCTION call");
@@ -533,10 +540,10 @@ fir::ExtendedValue Fortran::lower::genCallOpAndResult(
533540
funcType.getResults()[0].dyn_cast<fir::CharacterType>();
534541
mlir::Value len = builder.createIntegerConstant(
535542
loc, builder.getCharacterLengthType(), charTy.getLen());
536-
return fir::CharBoxValue{callResult, len};
543+
return {fir::CharBoxValue{callResult, len}, /*resultIsFinalized=*/false};
537544
}
538545

539-
return callResult;
546+
return {callResult, /*resultIsFinalized=*/false};
540547
}
541548

542549
static hlfir::EntityWithAttributes genStmtFunctionRef(
@@ -1389,7 +1396,7 @@ genUserCall(Fortran::lower::PreparedActualArguments &loweredActuals,
13891396
// Prepare lowered arguments according to the interface
13901397
// and map the lowered values to the dummy
13911398
// arguments.
1392-
fir::ExtendedValue result = Fortran::lower::genCallOpAndResult(
1399+
auto [result, resultIsFinalized] = Fortran::lower::genCallOpAndResult(
13931400
loc, callContext.converter, callContext.symMap, callContext.stmtCtx,
13941401
caller, callSiteType, callContext.resultType,
13951402
callContext.isElementalProcWithArrayArgs());
@@ -1404,24 +1411,43 @@ genUserCall(Fortran::lower::PreparedActualArguments &loweredActuals,
14041411
if (!fir::getBase(result))
14051412
return std::nullopt; // subroutine call.
14061413

1407-
hlfir::Entity resultEntity =
1408-
extendedValueToHlfirEntity(loc, builder, result, ".tmp.func_result");
1414+
if (fir::isPointerType(fir::getBase(result).getType()))
1415+
return extendedValueToHlfirEntity(loc, builder, result, tempResultName);
14091416

1410-
if (!fir::isPointerType(fir::getBase(result).getType())) {
1417+
if (!resultIsFinalized) {
1418+
hlfir::Entity resultEntity =
1419+
extendedValueToHlfirEntity(loc, builder, result, tempResultName);
14111420
resultEntity = loadTrivialScalar(loc, builder, resultEntity);
1412-
14131421
if (resultEntity.isVariable()) {
1414-
// Function result must not be freed, since it is allocated on the stack.
1415-
// Note that in non-elemental case, genCallOpAndResult()
1416-
// is responsible for establishing the clean-up that destroys
1417-
// the derived type result or deallocates its components
1418-
// without finalization.
1422+
// If the result has no finalization, it can be moved into an expression.
1423+
// In such case, the expression should not be freed after its use since
1424+
// the result is stack allocated or deallocation (for allocatable results)
1425+
// was already inserted in genCallOpAndResult.
14191426
auto asExpr = builder.create<hlfir::AsExprOp>(
14201427
loc, resultEntity, /*mustFree=*/builder.createBool(loc, false));
1421-
resultEntity = hlfir::EntityWithAttributes{asExpr.getResult()};
1428+
return hlfir::EntityWithAttributes{asExpr.getResult()};
14221429
}
1430+
return hlfir::EntityWithAttributes{resultEntity};
14231431
}
1424-
return hlfir::EntityWithAttributes{resultEntity};
1432+
// If the result has finalization, it cannot be moved because use of its
1433+
// value have been created in the statement context and may be emitted
1434+
// after the hlfir.expr destroy, so the result is kept as a variable in
1435+
// HLFIR. This may lead to copies when passing the result to an argument
1436+
// with VALUE, and this do not convey the fact that the result will not
1437+
// change, but is correct, and using hlfir.expr without the move would
1438+
// trigger a copy that may be avoided.
1439+
1440+
// Load allocatable results before emitting the hlfir.declare and drop its
1441+
// lower bounds: this is not a variable From the Fortran point of view, so
1442+
// the lower bounds are ones when inquired on the caller side.
1443+
const auto *allocatable = result.getBoxOf<fir::MutableBoxValue>();
1444+
fir::ExtendedValue loadedResult =
1445+
allocatable
1446+
? fir::factory::genMutableBoxRead(builder, loc, *allocatable,
1447+
/*mayBePolymorphic=*/true,
1448+
/*preserveLowerBounds=*/false)
1449+
: result;
1450+
return extendedValueToHlfirEntity(loc, builder, loadedResult, tempResultName);
14251451
}
14261452

14271453
/// Create an optional dummy argument value from an entity that may be

flang/lib/Lower/ConvertExpr.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2846,8 +2846,10 @@ class ScalarExprLowering {
28462846
}
28472847
}
28482848

2849-
ExtValue result = Fortran::lower::genCallOpAndResult(
2850-
loc, converter, symMap, stmtCtx, caller, callSiteType, resultType);
2849+
ExtValue result =
2850+
Fortran::lower::genCallOpAndResult(loc, converter, symMap, stmtCtx,
2851+
caller, callSiteType, resultType)
2852+
.first;
28512853

28522854
// Sync pointers and allocatables that may have been modified during the
28532855
// call.
@@ -4866,8 +4868,10 @@ class ArrayExprLowering {
48664868
[&](const auto &) { return fir::getBase(exv); });
48674869
caller.placeInput(argIface, arg);
48684870
}
4869-
return Fortran::lower::genCallOpAndResult(
4870-
loc, converter, symMap, getElementCtx(), caller, callSiteType, retTy);
4871+
return Fortran::lower::genCallOpAndResult(loc, converter, symMap,
4872+
getElementCtx(), caller,
4873+
callSiteType, retTy)
4874+
.first;
48714875
};
48724876
}
48734877

flang/lib/Optimizer/Builder/MutableBox.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,22 +406,26 @@ static bool readToBoxValue(const fir::MutableBoxValue &box,
406406
fir::ExtendedValue
407407
fir::factory::genMutableBoxRead(fir::FirOpBuilder &builder, mlir::Location loc,
408408
const fir::MutableBoxValue &box,
409-
bool mayBePolymorphic) {
409+
bool mayBePolymorphic,
410+
bool preserveLowerBounds) {
410411
if (box.hasAssumedRank())
411412
TODO(loc, "assumed rank allocatables or pointers");
412413
llvm::SmallVector<mlir::Value> lbounds;
413414
llvm::SmallVector<mlir::Value> extents;
414415
llvm::SmallVector<mlir::Value> lengths;
415416
if (readToBoxValue(box, mayBePolymorphic)) {
416417
auto reader = MutablePropertyReader(builder, loc, box);
417-
reader.getLowerBounds(lbounds);
418+
if (preserveLowerBounds)
419+
reader.getLowerBounds(lbounds);
418420
return fir::BoxValue{reader.getIrBox(), lbounds,
419421
box.nonDeferredLenParams()};
420422
}
421423
// Contiguous intrinsic type entity: all the data can be extracted from the
422424
// fir.box.
423425
auto addr =
424426
MutablePropertyReader(builder, loc, box).read(lbounds, extents, lengths);
427+
if (!preserveLowerBounds)
428+
lbounds.clear();
425429
auto rank = box.rank();
426430
if (box.isCharacter()) {
427431
auto len = lengths.empty() ? mlir::Value{} : lengths[0];

flang/test/Lower/HLFIR/function-return-as-expr.f90

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,26 @@ function inner()
6666
end function inner
6767
end subroutine test4
6868
! CHECK-LABEL: func.func @_QPtest4() {
69-
! CHECK: %[[VAL_6:.*]]:2 = hlfir.declare %{{.*}} {uniq_name = ".tmp.func_result"} : (!fir.ref<!fir.class<!fir.heap<none>>>) -> (!fir.ref<!fir.class<!fir.heap<none>>>, !fir.ref<!fir.class<!fir.heap<none>>>)
70-
! CHECK: %[[VAL_7:.*]] = fir.load %[[VAL_6]]#0 : !fir.ref<!fir.class<!fir.heap<none>>>
71-
! CHECK: %[[VAL_8:.*]] = arith.constant false
72-
! CHECK: %[[VAL_9:.*]] = hlfir.as_expr %[[VAL_7]] move %[[VAL_8]] : (!fir.class<!fir.heap<none>>, i1) -> !hlfir.expr<none?>
73-
! CHECK: hlfir.assign %[[VAL_9]] to %{{.*}}#0 realloc : !hlfir.expr<none?>, !fir.ref<!fir.class<!fir.heap<none>>>
69+
! CHECK: %[[VAL_6:.*]] = fir.load %[[VAL_0:.*]] : !fir.ref<!fir.class<!fir.heap<none>>>
70+
! CHECK: %[[VAL_7:.*]]:2 = hlfir.declare %[[VAL_6]] {uniq_name = ".tmp.func_result"} : (!fir.class<!fir.heap<none>>) -> (!fir.class<!fir.heap<none>>, !fir.class<!fir.heap<none>>)
71+
! CHECK: hlfir.assign %[[VAL_7]]#0 to %{{.*}}#0 realloc : !fir.class<!fir.heap<none>>, !fir.ref<!fir.class<!fir.heap<none>>>
72+
! CHECK: %[[VAL_10:.*]] = fir.convert %[[VAL_0]] : (!fir.ref<!fir.class<!fir.heap<none>>>) -> !fir.box<none>
73+
! CHECK: fir.call @_FortranADestroy(%[[VAL_10]]) fastmath<contract> : (!fir.box<none>) -> none
74+
75+
subroutine test4b
76+
class(*), allocatable :: p(:, :)
77+
p = inner()
78+
contains
79+
function inner()
80+
class(*), allocatable :: inner(:, :)
81+
end function inner
82+
end subroutine test4b
83+
! CHECK-LABEL: func.func @_QPtest4b() {
84+
! CHECK: %[[VAL_6:.*]] = fir.load %[[VAL_0:.*]] : !fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>
85+
! CHECK: %[[VAL_7:.*]]:2 = hlfir.declare %[[VAL_6]] {uniq_name = ".tmp.func_result"} : (!fir.class<!fir.heap<!fir.array<?x?xnone>>>) -> (!fir.class<!fir.heap<!fir.array<?x?xnone>>>, !fir.class<!fir.heap<!fir.array<?x?xnone>>>)
86+
! CHECK: hlfir.assign %[[VAL_7]]#0 to %{{.*}}#0 realloc : !fir.class<!fir.heap<!fir.array<?x?xnone>>>, !fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>
87+
! CHECK: %[[VAL_10:.*]] = fir.convert %[[VAL_0]] : (!fir.ref<!fir.class<!fir.heap<!fir.array<?x?xnone>>>>) -> !fir.box<none>
88+
! CHECK: fir.call @_FortranADestroy(%[[VAL_10]]) fastmath<contract> : (!fir.box<none>) -> none
7489

7590
subroutine test5
7691
use types

0 commit comments

Comments
 (0)