Skip to content

Commit

Permalink
[flang][hlfir] Fixed byval passing for dynamically optional intrinsic…
Browse files Browse the repository at this point in the history
… args.

In the context of elemental operation a dynamically optional
intrinsic argument must be lowered such that the elemental
designator is generated under isPresent check.

Reviewed By: tblah

Differential Revision: https://reviews.llvm.org/D154897
  • Loading branch information
vzakhari committed Jul 11, 2023
1 parent caf5b6a commit 5c42807
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 11 deletions.
46 changes: 35 additions & 11 deletions flang/lib/Lower/ConvertCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1192,20 +1192,26 @@ genUserCall(Fortran::lower::PreparedActualArguments &loweredActuals,
return extendedValueToHlfirEntity(loc, builder, result, ".tmp.func_result");
}

/// Create an optional dummy argument value from \p entity that may be
/// absent. This can only be called with numerical or logical scalar \p entity.
/// If \p entity is considered absent according to 15.5.2.12 point 1., the
/// returned value is zero (or false), otherwise it is the value of \p entity.
/// Create an optional dummy argument value from an entity that may be
/// absent. \p actualGetter callback returns hlfir::Entity denoting
/// the lowered actual argument. \p actualGetter can only return numerical
/// or logical scalar entity.
/// If the entity is considered absent according to 15.5.2.12 point 1., the
/// returned value is zero (or false), otherwise it is the value of the entity.
/// \p eleType specifies the entity's Fortran element type.
template <typename T>
static ExvAndCleanup genOptionalValue(fir::FirOpBuilder &builder,
mlir::Location loc, hlfir::Entity entity,
mlir::Value isPresent) {
mlir::Type eleType = entity.getFortranElementType();
assert(entity.isScalar() && fir::isa_trivial(eleType) &&
"must be a numerical or logical scalar");
mlir::Location loc, mlir::Type eleType,
T actualGetter, mlir::Value isPresent) {
return {builder
.genIfOp(loc, {eleType}, isPresent,
/*withElseRegion=*/true)
.genThen([&]() {
hlfir::Entity entity = actualGetter(loc, builder);
assert(eleType == entity.getFortranElementType() &&
"result type mismatch in genOptionalValue");
assert(entity.isScalar() && fir::isa_trivial(eleType) &&
"must be a numerical or logical scalar");
mlir::Value val =
hlfir::loadTrivialScalar(loc, builder, entity);
builder.create<fir::ResultOp>(loc, val);
Expand Down Expand Up @@ -1288,9 +1294,9 @@ genIntrinsicRefCore(Fortran::lower::PreparedActualArguments &loweredActuals,
operands.emplace_back(fir::getAbsentIntrinsicArgument());
continue;
}
hlfir::Entity actual = arg.value()->getActual(loc, builder);
if (!argLowering) {
// No argument lowering instruction, lower by value.
hlfir::Entity actual = arg.value()->getActual(loc, builder);
operands.emplace_back(
Fortran::lower::convertToValue(loc, converter, actual, stmtCtx));
continue;
Expand All @@ -1316,24 +1322,40 @@ genIntrinsicRefCore(Fortran::lower::PreparedActualArguments &loweredActuals,
mlir::Value isPresent = arg.value()->getIsPresent();
switch (argRules.lowerAs) {
case fir::LowerIntrinsicArgAs::Value: {
auto [exv, cleanup] = genOptionalValue(builder, loc, actual, isPresent);
// In case of elemental call, getActual() may produce
// a designator denoting the array element to be passed
// to the subprogram. If the actual array is dynamically
// optional the designator must be generated under
// isPresent check, because the box bounds reads will be
// generated in the codegen. These reads are illegal,
// if the dynamically optional argument is absent.
auto getActualCb = [&](mlir::Location loc,
fir::FirOpBuilder &builder) -> hlfir::Entity {
return arg.value()->getActual(loc, builder);
};
auto [exv, cleanup] =
genOptionalValue(builder, loc, getActualFortranElementType(),
getActualCb, isPresent);
addToCleanups(std::move(cleanup));
operands.emplace_back(exv);
continue;
}
case fir::LowerIntrinsicArgAs::Addr: {
hlfir::Entity actual = arg.value()->getActual(loc, builder);
auto [exv, cleanup] = genOptionalAddr(builder, loc, actual, isPresent);
addToCleanups(std::move(cleanup));
operands.emplace_back(exv);
continue;
}
case fir::LowerIntrinsicArgAs::Box: {
hlfir::Entity actual = arg.value()->getActual(loc, builder);
auto [exv, cleanup] = genOptionalBox(builder, loc, actual, isPresent);
addToCleanups(std::move(cleanup));
operands.emplace_back(exv);
continue;
}
case fir::LowerIntrinsicArgAs::Inquired: {
hlfir::Entity actual = arg.value()->getActual(loc, builder);
auto [exv, cleanup] =
hlfir::translateToExtendedValue(loc, builder, actual);
addToCleanups(std::move(cleanup));
Expand All @@ -1343,6 +1365,8 @@ genIntrinsicRefCore(Fortran::lower::PreparedActualArguments &loweredActuals,
}
llvm_unreachable("bad switch");
}

hlfir::Entity actual = arg.value()->getActual(loc, builder);
switch (argRules.lowerAs) {
case fir::LowerIntrinsicArgAs::Value:
operands.emplace_back(
Expand Down
45 changes: 45 additions & 0 deletions flang/test/Lower/HLFIR/intrinsic-dynamically-optional.f90
Original file line number Diff line number Diff line change
Expand Up @@ -153,5 +153,50 @@ subroutine test_optional_as_addr
! CHECK: return
! CHECK: }

! imaginary component is dyamically optional, lowered as a value
! Test placement of the designator under isPresent check.
function test_elemental_optional_as_value(real, imaginary)
real :: real(3)
real, optional :: imaginary(3)
complex :: test_elemental_optional_as_value(3)
test_elemental_optional_as_value = cmplx(real, imaginary)
end function
! CHECK-LABEL: func.func @_QPtest_elemental_optional_as_value(
! CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.array<3xf32>> {fir.bindc_name = "real"},
! CHECK-SAME: %[[VAL_1:.*]]: !fir.ref<!fir.array<3xf32>> {fir.bindc_name = "imaginary", fir.optional}) -> !fir.array<3x!fir.complex<4>> {
! CHECK: %[[VAL_2:.*]] = arith.constant 3 : index
! CHECK: %[[VAL_3:.*]] = fir.shape %[[VAL_2]] : (index) -> !fir.shape<1>
! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[VAL_1]](%[[VAL_3]]) {fortran_attrs = #fir.var_attrs<optional>, uniq_name = "_QFtest_elemental_optional_as_valueEimaginary"} : (!fir.ref<!fir.array<3xf32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<3xf32>>, !fir.ref<!fir.array<3xf32>>)
! CHECK: %[[VAL_5:.*]] = arith.constant 3 : index
! CHECK: %[[VAL_6:.*]] = fir.shape %[[VAL_5]] : (index) -> !fir.shape<1>
! CHECK: %[[VAL_7:.*]]:2 = hlfir.declare %[[VAL_0]](%[[VAL_6]]) {uniq_name = "_QFtest_elemental_optional_as_valueEreal"} : (!fir.ref<!fir.array<3xf32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<3xf32>>, !fir.ref<!fir.array<3xf32>>)
! CHECK: %[[VAL_8:.*]] = arith.constant 3 : index
! CHECK: %[[VAL_9:.*]] = fir.alloca !fir.array<3x!fir.complex<4>> {bindc_name = "test_elemental_optional_as_value", uniq_name = "_QFtest_elemental_optional_as_valueEtest_elemental_optional_as_value"}
! CHECK: %[[VAL_10:.*]] = fir.shape %[[VAL_8]] : (index) -> !fir.shape<1>
! CHECK: %[[VAL_11:.*]]:2 = hlfir.declare %[[VAL_9]](%[[VAL_10]]) {uniq_name = "_QFtest_elemental_optional_as_valueEtest_elemental_optional_as_value"} : (!fir.ref<!fir.array<3x!fir.complex<4>>>, !fir.shape<1>) -> (!fir.ref<!fir.array<3x!fir.complex<4>>>, !fir.ref<!fir.array<3x!fir.complex<4>>>)
! CHECK: %[[VAL_12:.*]] = fir.is_present %[[VAL_4]]#0 : (!fir.ref<!fir.array<3xf32>>) -> i1
! CHECK: %[[VAL_13:.*]] = hlfir.elemental %[[VAL_6]] unordered : (!fir.shape<1>) -> !hlfir.expr<3x!fir.complex<4>> {
! CHECK: ^bb0(%[[VAL_14:.*]]: index):
! CHECK: %[[VAL_15:.*]] = hlfir.designate %[[VAL_7]]#0 (%[[VAL_14]]) : (!fir.ref<!fir.array<3xf32>>, index) -> !fir.ref<f32>
! CHECK: %[[VAL_16:.*]] = fir.load %[[VAL_15]] : !fir.ref<f32>
! CHECK: %[[VAL_17:.*]] = fir.if %[[VAL_12]] -> (f32) {
! CHECK: %[[VAL_18:.*]] = hlfir.designate %[[VAL_4]]#0 (%[[VAL_14]]) : (!fir.ref<!fir.array<3xf32>>, index) -> !fir.ref<f32>
! CHECK: %[[VAL_19:.*]] = fir.load %[[VAL_18]] : !fir.ref<f32>
! CHECK: fir.result %[[VAL_19]] : f32
! CHECK: } else {
! CHECK: %[[VAL_20:.*]] = arith.constant 0.000000e+00 : f32
! CHECK: fir.result %[[VAL_20]] : f32
! CHECK: }
! CHECK: %[[VAL_21:.*]] = fir.undefined !fir.complex<4>
! CHECK: %[[VAL_22:.*]] = fir.insert_value %[[VAL_21]], %[[VAL_16]], [0 : index] : (!fir.complex<4>, f32) -> !fir.complex<4>
! CHECK: %[[VAL_23:.*]] = fir.insert_value %[[VAL_22]], %[[VAL_17]], [1 : index] : (!fir.complex<4>, f32) -> !fir.complex<4>
! CHECK: hlfir.yield_element %[[VAL_23]] : !fir.complex<4>
! CHECK: }
! CHECK: hlfir.assign %[[VAL_13]] to %[[VAL_11]]#0 : !hlfir.expr<3x!fir.complex<4>>, !fir.ref<!fir.array<3x!fir.complex<4>>>
! CHECK: hlfir.destroy %[[VAL_13]] : !hlfir.expr<3x!fir.complex<4>>
! CHECK: %[[VAL_24:.*]] = fir.load %[[VAL_11]]#1 : !fir.ref<!fir.array<3x!fir.complex<4>>>
! CHECK: return %[[VAL_24]] : !fir.array<3x!fir.complex<4>>
! CHECK: }

! TODO: there seem to be no intrinsics with dynamically optional arguments lowered asInquired

0 comments on commit 5c42807

Please sign in to comment.