Skip to content

Commit c232137

Browse files
authored
[flang][HLFIR] compute elemental function result length parameters (#93983)
Prepare the argument and map them to their corresponding dummy symbol in order to lower the specification expression of the function result. Extract the preparation of arguments according to the interface to its own function to be reused. It seems there is no need to conditionally compute the length on the input since all the information comes from the CharBoxValue or the descriptor for cases where the number of element could be 0.
1 parent 9482af3 commit c232137

File tree

2 files changed

+159
-13
lines changed

2 files changed

+159
-13
lines changed

flang/lib/Lower/ConvertCall.cpp

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1457,21 +1457,16 @@ static PreparedDummyArgument prepareProcedurePointerActualArgument(
14571457
return PreparedDummyArgument{tempBoxProc, /*cleanups=*/{}};
14581458
}
14591459

1460-
/// Lower calls to user procedures with actual arguments that have been
1461-
/// pre-lowered but not yet prepared according to the interface.
1462-
/// This can be called for elemental procedures, but only with scalar
1463-
/// arguments: if there are array arguments, it must be provided with
1464-
/// the array argument elements value and will return the corresponding
1465-
/// scalar result value.
1466-
static std::optional<hlfir::EntityWithAttributes>
1467-
genUserCall(Fortran::lower::PreparedActualArguments &loweredActuals,
1468-
Fortran::lower::CallerInterface &caller,
1469-
mlir::FunctionType callSiteType, CallContext &callContext) {
1460+
/// Prepare arguments of calls to user procedures with actual arguments that
1461+
/// have been pre-lowered but not yet prepared according to the interface.
1462+
void prepareUserCallArguments(
1463+
Fortran::lower::PreparedActualArguments &loweredActuals,
1464+
Fortran::lower::CallerInterface &caller, mlir::FunctionType callSiteType,
1465+
CallContext &callContext, llvm::SmallVector<CallCleanUp> &callCleanUps) {
14701466
using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
14711467
mlir::Location loc = callContext.loc;
14721468
bool mustRemapActualToDummyDescriptors = false;
14731469
fir::FirOpBuilder &builder = callContext.getBuilder();
1474-
llvm::SmallVector<CallCleanUp> callCleanUps;
14751470
for (auto [preparedActual, arg] :
14761471
llvm::zip(loweredActuals, caller.getPassedArguments())) {
14771472
mlir::Type argTy = callSiteType.getInput(arg.firArgument);
@@ -1629,11 +1624,30 @@ genUserCall(Fortran::lower::PreparedActualArguments &loweredActuals,
16291624
} break;
16301625
}
16311626
}
1627+
16321628
// Handle cases where caller must allocate the result or a fir.box for it.
16331629
if (mustRemapActualToDummyDescriptors)
16341630
remapActualToDummyDescriptors(loc, callContext.converter,
16351631
callContext.symMap, loweredActuals, caller,
16361632
callContext.isBindcCall());
1633+
}
1634+
1635+
/// Lower calls to user procedures with actual arguments that have been
1636+
/// pre-lowered but not yet prepared according to the interface.
1637+
/// This can be called for elemental procedures, but only with scalar
1638+
/// arguments: if there are array arguments, it must be provided with
1639+
/// the array argument elements value and will return the corresponding
1640+
/// scalar result value.
1641+
static std::optional<hlfir::EntityWithAttributes>
1642+
genUserCall(Fortran::lower::PreparedActualArguments &loweredActuals,
1643+
Fortran::lower::CallerInterface &caller,
1644+
mlir::FunctionType callSiteType, CallContext &callContext) {
1645+
mlir::Location loc = callContext.loc;
1646+
llvm::SmallVector<CallCleanUp> callCleanUps;
1647+
fir::FirOpBuilder &builder = callContext.getBuilder();
1648+
1649+
prepareUserCallArguments(loweredActuals, caller, callSiteType, callContext,
1650+
callCleanUps);
16371651

16381652
// Prepare lowered arguments according to the interface
16391653
// and map the lowered values to the dummy
@@ -2208,8 +2222,45 @@ class ElementalUserCallBuilder
22082222
mlir::Value computeDynamicCharacterResultLength(
22092223
Fortran::lower::PreparedActualArguments &loweredActuals,
22102224
CallContext &callContext) {
2211-
TODO(callContext.loc,
2212-
"compute elemental function result length parameters in HLFIR");
2225+
fir::FirOpBuilder &builder = callContext.getBuilder();
2226+
mlir::Location loc = callContext.loc;
2227+
auto &converter = callContext.converter;
2228+
mlir::Type idxTy = builder.getIndexType();
2229+
llvm::SmallVector<CallCleanUp> callCleanUps;
2230+
2231+
prepareUserCallArguments(loweredActuals, caller, callSiteType, callContext,
2232+
callCleanUps);
2233+
2234+
callContext.symMap.pushScope();
2235+
2236+
// Map prepared argument to dummy symbol to be able to lower spec expr.
2237+
for (const auto &arg : caller.getPassedArguments()) {
2238+
const Fortran::semantics::Symbol *sym = caller.getDummySymbol(arg);
2239+
assert(sym && "expect symbol for dummy argument");
2240+
auto input = caller.getInput(arg);
2241+
fir::ExtendedValue exv = Fortran::lower::translateToExtendedValue(
2242+
loc, builder, hlfir::Entity{input}, callContext.stmtCtx);
2243+
fir::FortranVariableOpInterface variableIface = hlfir::genDeclare(
2244+
loc, builder, exv, "dummy.tmp", fir::FortranVariableFlagsAttr{});
2245+
callContext.symMap.addVariableDefinition(*sym, variableIface);
2246+
}
2247+
2248+
auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value {
2249+
mlir::Value convertExpr = builder.createConvert(
2250+
loc, idxTy,
2251+
fir::getBase(converter.genExprValue(expr, callContext.stmtCtx)));
2252+
return fir::factory::genMaxWithZero(builder, loc, convertExpr);
2253+
};
2254+
2255+
llvm::SmallVector<mlir::Value> lengths;
2256+
caller.walkResultLengths(
2257+
[&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) {
2258+
assert(!isAssumedSizeExtent && "result cannot be assumed-size");
2259+
lengths.emplace_back(lowerSpecExpr(e));
2260+
});
2261+
callContext.symMap.popScope();
2262+
assert(lengths.size() == 1 && "expect 1 length parameter for the result");
2263+
return lengths[0];
22132264
}
22142265

22152266
mlir::Value getPolymorphicResultMold(
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
! RUN: bbc -emit-hlfir -o - %s | fir-opt --canonicalize | FileCheck %s
2+
3+
module m1
4+
contains
5+
elemental function fct1(a, b) result(t)
6+
character(*), intent(in) :: a, b
7+
character(len(a) + len(b)) :: t
8+
t = a // b
9+
end function
10+
11+
elemental function fct2(c) result(t)
12+
integer, intent(in) :: c
13+
character(c) :: t
14+
15+
end function
16+
17+
subroutine sub2(a,b,c)
18+
character(*), intent(inout) :: c
19+
character(*), intent(in) :: a, b
20+
21+
c = fct1(a,b)
22+
end subroutine
23+
24+
! CHECK-LABEL: func.func @_QMm1Psub2(
25+
! CHECK-SAME: %[[ARG0:.*]]: !fir.boxchar<1> {fir.bindc_name = "a"}, %[[ARG1:.*]]: !fir.boxchar<1> {fir.bindc_name = "b"}, %[[ARG2:.*]]: !fir.boxchar<1> {fir.bindc_name = "c"}) {
26+
! CHECK: %[[UNBOX_ARG0:.*]]:2 = fir.unboxchar %[[ARG0]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
27+
! CHECK: %[[A:.*]]:2 = hlfir.declare %[[UNBOX_ARG0]]#0 typeparams %[[UNBOX_ARG0]]#1 dummy_scope %0 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMm1Fsub2Ea"} : (!fir.ref<!fir.char<1,?>>, index, !fir.dscope) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
28+
! CHECK: %[[UNBOX_ARG1:.*]]:2 = fir.unboxchar %[[ARG1]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
29+
! CHECK: %[[B:.*]]:2 = hlfir.declare %[[UNBOX_ARG1]]#0 typeparams %[[UNBOX_ARG1]]#1 dummy_scope %{{.*}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMm1Fsub2Eb"} : (!fir.ref<!fir.char<1,?>>, index, !fir.dscope) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
30+
! CHECK: %[[UNBOX_ARG2:.*]]:2 = fir.unboxchar %[[ARG2]] : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
31+
! CHECK: %[[C:.*]]:2 = hlfir.declare %[[UNBOX_ARG2]]#0 typeparams %[[UNBOX_ARG2]]#1 dummy_scope %{{.*}} {fortran_attrs = #fir.var_attrs<intent_inout>, uniq_name = "_QMm1Fsub2Ec"} : (!fir.ref<!fir.char<1,?>>, index, !fir.dscope) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
32+
! CHECK: %[[UNBOX_A:.*]]:2 = fir.unboxchar %[[A]]#0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
33+
! CHECK: %[[DUMMYA:.*]]:2 = hlfir.declare %[[UNBOX_A]]#0 typeparams %[[UNBOX_A]]#1 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMm1Ffct1Ea"} : (!fir.ref<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
34+
! CHECK: %[[UNBOX_B:.*]]:2 = fir.unboxchar %[[B]]#0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
35+
! CHECK: %[[DUMMYB:.*]]:2 = hlfir.declare %[[UNBOX_B]]#0 typeparams %[[UNBOX_B]]#1 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMm1Ffct1Eb"} : (!fir.ref<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>)
36+
! CHECK: %[[LEN_A:.*]] = fir.convert %[[UNBOX_A]]#1 : (index) -> i32
37+
! CHECK: %[[LEN_B:.*]] = fir.convert %[[UNBOX_B]]#1 : (index) -> i32
38+
! CHECK: %[[LEN_LEN:.*]] = arith.addi %[[LEN_A]], %[[LEN_B]] : i32
39+
! CHECK: %[[LEN_LEN_IDX:.*]] = fir.convert %[[LEN_LEN]] : (i32) -> index
40+
! CHECK: %[[CMPI:.*]] = arith.cmpi sgt, %[[LEN_LEN_IDX]], %c0{{.*}} : index
41+
! CHECK: %[[RES_LENGTH:.*]] = arith.select %[[CMPI]], %[[LEN_LEN_IDX]], %c0{{.*}} : index
42+
! CHECK: %[[RES:.*]] = fir.alloca !fir.char<1,?>(%[[RES_LENGTH]] : index) {bindc_name = ".result"}
43+
! CHECK: fir.call @_QMm1Pfct1
44+
45+
subroutine sub3(c)
46+
character(*), intent(inout) :: c(:)
47+
48+
c = fct2(10)
49+
end subroutine
50+
51+
! CHECK-LABEL: func.func @_QMm1Psub3(
52+
! CHECK-SAME: %[[ARG0:.*]]: !fir.box<!fir.array<?x!fir.char<1,?>>> {fir.bindc_name = "c"}) {
53+
! CHECK: %[[C10:.*]] = arith.constant 10 : i32
54+
! CHECK: %[[C:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{.*}} {fortran_attrs = #fir.var_attrs<intent_inout>, uniq_name = "_QMm1Fsub3Ec"} : (!fir.box<!fir.array<?x!fir.char<1,?>>>, !fir.dscope) -> (!fir.box<!fir.array<?x!fir.char<1,?>>>, !fir.box<!fir.array<?x!fir.char<1,?>>>)
55+
! CHECK: %[[ASSOC:.*]]:3 = hlfir.associate %[[C10]] {adapt.valuebyref} : (i32) -> (!fir.ref<i32>, !fir.ref<i32>, i1)
56+
! CHECK: %[[INPUT_ARG0:.*]]:2 = hlfir.declare %[[ASSOC]]#1 {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMm1Ffct2Ec"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
57+
! CHECK: %[[LOAD_INPUT_ARG0:.*]] = fir.load %[[INPUT_ARG0]]#0 : !fir.ref<i32>
58+
! CHECK: %[[LOAD_INPUT_ARG0_IDX:.*]] = fir.convert %[[LOAD_INPUT_ARG0]] : (i32) -> index
59+
! CHECK: %[[CMPI:.*]] = arith.cmpi sgt, %[[LOAD_INPUT_ARG0_IDX]], %c0{{.*}} : index
60+
! CHECK: %[[LENGTH:.*]] = arith.select %[[CMPI]], %[[LOAD_INPUT_ARG0_IDX]], %c0{{.*}} : index
61+
! CHECK: %[[RES:.*]] = fir.alloca !fir.char<1,?>(%[[LENGTH]] : index) {bindc_name = ".result"}
62+
! CHECK: fir.call @_QMm1Pfct2
63+
64+
subroutine sub4(a,b,c)
65+
character(*), intent(inout) :: c(:)
66+
character(*), intent(in) :: a(:), b(:)
67+
68+
c = fct1(a,b)
69+
end subroutine
70+
71+
! CHECK-LABEL: func.func @_QMm1Psub4(
72+
! CHECK-SAME: %[[ARG0:.*]]: !fir.box<!fir.array<?x!fir.char<1,?>>> {fir.bindc_name = "a"}, %[[ARG1:.*]]: !fir.box<!fir.array<?x!fir.char<1,?>>> {fir.bindc_name = "b"}, %[[ARG2:.*]]: !fir.box<!fir.array<?x!fir.char<1,?>>> {fir.bindc_name = "c"}) {
73+
! CHECK: %[[A:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{.*}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMm1Fsub4Ea"} : (!fir.box<!fir.array<?x!fir.char<1,?>>>, !fir.dscope) -> (!fir.box<!fir.array<?x!fir.char<1,?>>>, !fir.box<!fir.array<?x!fir.char<1,?>>>)
74+
! CHECK: %[[B:.*]]:2 = hlfir.declare %[[ARG1]] dummy_scope %{{.*}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMm1Fsub4Eb"} : (!fir.box<!fir.array<?x!fir.char<1,?>>>, !fir.dscope) -> (!fir.box<!fir.array<?x!fir.char<1,?>>>, !fir.box<!fir.array<?x!fir.char<1,?>>>)
75+
! CHECK: %[[C:.*]]:2 = hlfir.declare %[[ARG2]] dummy_scope %{{.*}} {fortran_attrs = #fir.var_attrs<intent_inout>, uniq_name = "_QMm1Fsub4Ec"} : (!fir.box<!fir.array<?x!fir.char<1,?>>>, !fir.dscope) -> (!fir.box<!fir.array<?x!fir.char<1,?>>>, !fir.box<!fir.array<?x!fir.char<1,?>>>)
76+
! CHECK: %[[LEN_A:.*]] = fir.box_elesize %[[A]]#1 : (!fir.box<!fir.array<?x!fir.char<1,?>>>) -> index
77+
! CHECK: %[[LEN_B:.*]] = fir.box_elesize %[[B]]#1 : (!fir.box<!fir.array<?x!fir.char<1,?>>>) -> index
78+
! CHECK: %[[LEN_A_I32:.*]] = fir.convert %[[LEN_A]] : (index) -> i32
79+
! CHECK: %[[LEN_B_I32:.*]] = fir.convert %[[LEN_B]] : (index) -> i32
80+
! CHECK: %[[LEN_LEN:.*]] = arith.addi %[[LEN_A_I32]], %[[LEN_B_I32]] : i32
81+
! CHECK: %[[LEN_LEN_IDX:.*]] = fir.convert %[[LEN_LEN]] : (i32) -> index
82+
! CHECK: %[[CMPI:.*]] = arith.cmpi sgt, %[[LEN_LEN_IDX]], %c0{{.*}} : index
83+
! CHECK: %[[LENGTH:.*]] = arith.select %[[CMPI]], %17, %c0{{.*}} : index
84+
! CHECK: %{{.*}} = hlfir.elemental %{{.*}} typeparams %[[LENGTH]] unordered : (!fir.shape<1>, index) -> !hlfir.expr<?x!fir.char<1,?>>
85+
86+
end module
87+
88+
program test
89+
use m1
90+
character(5) :: a(2) = ['abcde', 'klmnop'], b(2) = ['fghij', 'qrstu']
91+
character(10) :: c(2)
92+
93+
call sub2(a(1), b(1), c(1))
94+
print*, c(1)
95+
end

0 commit comments

Comments
 (0)