Skip to content

Commit a103b9b

Browse files
authored
[flang][lowering] fix vector subscripts in character elemental procedures (#156661)
Fixes #145151 Character elemental procedures require evaluating the result specification expression outside of the elemental loops when the function result length is not a constant. This is needed so that the array result storage can be allocated before the evaluation if a storage is needed. Because the result specification expression may depend on inquires about the dummy argument (but not usages of values thanks to F2023 C15121), some representation of the dummy must be created. Since this is an elemental call, this requires providing an element, and not the whole array actual argument (we only care about the properties of such element it does not matter which element is being used). The previous code was creating the element with a type cast from the base array, but this did not work with vector subscripted arrays where the lowering representation is more complex. This caused a compiler assert to fire. Switch the code to only copy the properties that can be inquired from the actual argument to the mock dummy (length parameters, dynamic type and presence). A mock one address is used instead of addressing the actual argument before the loop (one is used instead of NULL so that presence inquiry will work as expected for OPTIONAL arguments).
1 parent 04518e7 commit a103b9b

File tree

6 files changed

+470
-53
lines changed

6 files changed

+470
-53
lines changed

flang/include/flang/Lower/HlfirIntrinsics.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,17 @@ struct PreparedActualArgument {
105105
return typeParams[0];
106106
}
107107

108+
void genLengthParameters(mlir::Location loc, fir::FirOpBuilder &builder,
109+
llvm::SmallVectorImpl<mlir::Value> &result) {
110+
if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual)) {
111+
hlfir::genLengthParameters(loc, builder, *actualEntity, result);
112+
return;
113+
}
114+
for (mlir::Value len :
115+
std::get<hlfir::ElementalAddrOp>(actual).getTypeparams())
116+
result.push_back(len);
117+
}
118+
108119
/// When the argument is polymorphic, get mold value with the same dynamic
109120
/// type.
110121
mlir::Value getPolymorphicMold(mlir::Location loc) const {

flang/lib/Lower/ConvertCall.cpp

Lines changed: 151 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,16 @@ static void remapActualToDummyDescriptors(
287287
}
288288
}
289289

290+
static void
291+
getResultLengthFromElementalOp(fir::FirOpBuilder &builder,
292+
llvm::SmallVectorImpl<mlir::Value> &lengths) {
293+
auto elemental = llvm::dyn_cast_or_null<hlfir::ElementalOp>(
294+
builder.getInsertionBlock()->getParentOp());
295+
if (elemental)
296+
for (mlir::Value len : elemental.getTypeparams())
297+
lengths.push_back(len);
298+
}
299+
290300
std::pair<Fortran::lower::LoweredResult, bool>
291301
Fortran::lower::genCallOpAndResult(
292302
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
@@ -296,7 +306,13 @@ Fortran::lower::genCallOpAndResult(
296306
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
297307
using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
298308
bool mustPopSymMap = false;
299-
if (caller.mustMapInterfaceSymbolsForResult()) {
309+
310+
llvm::SmallVector<mlir::Value> resultLengths;
311+
if (isElemental)
312+
getResultLengthFromElementalOp(builder, resultLengths);
313+
if (caller.mustMapInterfaceSymbolsForResult() && resultLengths.empty()) {
314+
// Do not map the dummy symbols again inside the loop to compute elemental
315+
// function result whose length was already computed outside of the loop.
300316
symMap.pushScope();
301317
mustPopSymMap = true;
302318
Fortran::lower::mapCallInterfaceSymbolsForResult(converter, caller, symMap);
@@ -340,7 +356,6 @@ Fortran::lower::genCallOpAndResult(
340356
loc, idxTy, fir::getBase(converter.genExprValue(expr, stmtCtx)));
341357
return fir::factory::genMaxWithZero(builder, loc, convertExpr);
342358
};
343-
llvm::SmallVector<mlir::Value> resultLengths;
344359
mlir::Value arrayResultShape;
345360
hlfir::EvaluateInMemoryOp evaluateInMemory;
346361
auto allocatedResult = [&]() -> std::optional<fir::ExtendedValue> {
@@ -355,11 +370,16 @@ Fortran::lower::genCallOpAndResult(
355370
assert(!isAssumedSizeExtent && "result cannot be assumed-size");
356371
extents.emplace_back(lowerSpecExpr(e));
357372
});
358-
caller.walkResultLengths(
359-
[&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) {
360-
assert(!isAssumedSizeExtent && "result cannot be assumed-size");
361-
lengths.emplace_back(lowerSpecExpr(e));
362-
});
373+
if (resultLengths.empty()) {
374+
caller.walkResultLengths(
375+
[&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) {
376+
assert(!isAssumedSizeExtent && "result cannot be assumed-size");
377+
lengths.emplace_back(lowerSpecExpr(e));
378+
});
379+
} else {
380+
// Use lengths precomputed before elemental loops.
381+
lengths = resultLengths;
382+
}
363383

364384
// Result length parameters should not be provided to box storage
365385
// allocation and save_results, but they are still useful information to
@@ -2330,6 +2350,47 @@ class ElementalCallBuilder {
23302350
}
23312351
};
23322352

2353+
/// Helper for computing elemental function result specification
2354+
/// expressions that depends on dummy symbols. See
2355+
/// computeDynamicCharacterResultLength below.
2356+
static mlir::Value genMockDummyForElementalResultSpecifications(
2357+
fir::FirOpBuilder &builder, mlir::Location loc, mlir::Type dummyType,
2358+
Fortran::lower::PreparedActualArgument &preparedActual) {
2359+
// One is used as the mock address instead of NULL so that PRESENT inquires
2360+
// work (this is the only valid thing that specification can do with the
2361+
// address thanks to Fortran 2023 C15121).
2362+
mlir::Value one =
2363+
builder.createIntegerConstant(loc, builder.getIntPtrType(), 1);
2364+
if (auto boxCharType = llvm::dyn_cast<fir::BoxCharType>(dummyType)) {
2365+
mlir::Value addr = builder.createConvert(
2366+
loc, fir::ReferenceType::get(boxCharType.getEleTy()), one);
2367+
mlir::Value len = preparedActual.genCharLength(loc, builder);
2368+
return fir::EmboxCharOp::create(builder, loc, boxCharType, addr, len);
2369+
}
2370+
if (auto box = llvm::dyn_cast<fir::BaseBoxType>(dummyType)) {
2371+
mlir::Value addr =
2372+
builder.createConvert(loc, box.getBaseAddressType(), one);
2373+
llvm::SmallVector<mlir::Value> lenParams;
2374+
preparedActual.genLengthParameters(loc, builder, lenParams);
2375+
mlir::Value mold;
2376+
if (fir::isPolymorphicType(box))
2377+
mold = preparedActual.getPolymorphicMold(loc);
2378+
return fir::EmboxOp::create(builder, loc, box, addr,
2379+
/*shape=*/mlir::Value{},
2380+
/*slice=*/mlir::Value{}, lenParams, mold);
2381+
}
2382+
// Values of arguments should not be used in elemental procedure specification
2383+
// expressions as per C15121, so it makes no sense to have a specification
2384+
// expression requiring a symbol that is passed by value (there is no good
2385+
// value to create here).
2386+
assert(fir::isa_ref_type(dummyType) &&
2387+
(fir::isa_trivial(fir::unwrapRefType(dummyType)) ||
2388+
fir::isa_char(fir::unwrapRefType(dummyType))) &&
2389+
"Only expect symbols inquired in elemental procedure result "
2390+
"specifications to be passed in memory");
2391+
return builder.createConvert(loc, dummyType, one);
2392+
}
2393+
23332394
class ElementalUserCallBuilder
23342395
: public ElementalCallBuilder<ElementalUserCallBuilder> {
23352396
public:
@@ -2362,29 +2423,97 @@ class ElementalUserCallBuilder
23622423
mlir::Value computeDynamicCharacterResultLength(
23632424
Fortran::lower::PreparedActualArguments &loweredActuals,
23642425
CallContext &callContext) {
2426+
23652427
fir::FirOpBuilder &builder = callContext.getBuilder();
23662428
mlir::Location loc = callContext.loc;
23672429
auto &converter = callContext.converter;
2368-
mlir::Type idxTy = builder.getIndexType();
2369-
llvm::SmallVector<CallCleanUp> callCleanUps;
23702430

2371-
prepareUserCallArguments(loweredActuals, caller, callSiteType, callContext,
2372-
callCleanUps);
2431+
// Gather the dummy argument symbols required directly or indirectly to
2432+
// evaluate the result symbol specification expressions.
2433+
llvm::SmallPtrSet<const Fortran::semantics::Symbol *, 4>
2434+
requiredDummySymbols;
2435+
const Fortran::semantics::Symbol &result = caller.getResultSymbol();
2436+
for (Fortran::lower::pft::Variable var :
2437+
Fortran::lower::pft::getDependentVariableList(result))
2438+
if (var.hasSymbol()) {
2439+
const Fortran::semantics::Symbol &sym = var.getSymbol();
2440+
if (Fortran::semantics::IsDummy(sym) && sym.owner() == result.owner())
2441+
requiredDummySymbols.insert(&sym);
2442+
}
23732443

2374-
callContext.symMap.pushScope();
2444+
// Prepare mock FIR arguments for each dummy arguments required in the
2445+
// result specifications. These mock arguments will have the same properties
2446+
// (dynamic type and type parameters) as the actual arguments, except for
2447+
// the address. Such mock argument are needed because this evaluation is
2448+
// happening before the loop for the elemental call (the array result
2449+
// storage must be allocated before the loops if any is needed, so the
2450+
// result properties must be known before the loops). So it is not possible
2451+
// to just pick an element (like the first one) and use that because the
2452+
// normal argument preparation have effects (vector subscripted actual
2453+
// argument will require reading the vector subscript and VALUE arguments
2454+
// preparation involve copies of the data. This could cause segfaults in
2455+
// case of zero size arrays and is in general pointless extra computation
2456+
// since the data cannot be used in the specification expression as per
2457+
// C15121).
2458+
if (!requiredDummySymbols.empty()) {
2459+
const Fortran::semantics::SubprogramDetails *iface =
2460+
caller.getInterfaceDetails();
2461+
assert(iface && "interface must be explicit when result specification "
2462+
"depends upon dummy symbols");
2463+
for (auto [maybePreparedActual, arg, sym] : llvm::zip(
2464+
loweredActuals, caller.getPassedArguments(), iface->dummyArgs()))
2465+
if (requiredDummySymbols.contains(sym)) {
2466+
mlir::Type dummyType = callSiteType.getInput(arg.firArgument);
2467+
2468+
if (!maybePreparedActual.has_value()) {
2469+
mlir::Value mockArgValue =
2470+
fir::AbsentOp::create(builder, loc, dummyType);
2471+
caller.placeInput(arg, mockArgValue);
2472+
continue;
2473+
}
23752474

2376-
// Map prepared argument to dummy symbol to be able to lower spec expr.
2377-
for (const auto &arg : caller.getPassedArguments()) {
2378-
const Fortran::semantics::Symbol *sym = caller.getDummySymbol(arg);
2379-
assert(sym && "expect symbol for dummy argument");
2380-
auto input = caller.getInput(arg);
2381-
fir::ExtendedValue exv = Fortran::lower::translateToExtendedValue(
2382-
loc, builder, hlfir::Entity{input}, callContext.stmtCtx);
2383-
fir::FortranVariableOpInterface variableIface = hlfir::genDeclare(
2384-
loc, builder, exv, "dummy.tmp", fir::FortranVariableFlagsAttr{});
2385-
callContext.symMap.addVariableDefinition(*sym, variableIface);
2475+
Fortran::lower::PreparedActualArgument &preparedActual =
2476+
maybePreparedActual.value();
2477+
2478+
if (preparedActual.handleDynamicOptional()) {
2479+
mlir::Value isPresent = preparedActual.getIsPresent();
2480+
mlir::Value mockArgValue =
2481+
builder
2482+
.genIfOp(loc, {dummyType}, isPresent,
2483+
/*withElseRegion=*/true)
2484+
.genThen([&]() {
2485+
mlir::Value mockArgValue =
2486+
genMockDummyForElementalResultSpecifications(
2487+
builder, loc, dummyType, preparedActual);
2488+
fir::ResultOp::create(builder, loc, mockArgValue);
2489+
})
2490+
.genElse([&]() {
2491+
mlir::Value absent =
2492+
fir::AbsentOp::create(builder, loc, dummyType);
2493+
fir::ResultOp::create(builder, loc, absent);
2494+
})
2495+
.getResults()[0];
2496+
caller.placeInput(arg, mockArgValue);
2497+
} else {
2498+
mlir::Value mockArgValue =
2499+
genMockDummyForElementalResultSpecifications(
2500+
builder, loc, dummyType, preparedActual);
2501+
caller.placeInput(arg, mockArgValue);
2502+
}
2503+
}
23862504
}
23872505

2506+
// Map symbols required by the result specification expressions to SSA
2507+
// values. This will both finish mapping the mock value created above if
2508+
// any, and deal with any module/common block variables accessed in the
2509+
// specification expressions.
2510+
// Map prepared argument to dummy symbol to be able to lower spec expr.
2511+
callContext.symMap.pushScope();
2512+
Fortran::lower::mapCallInterfaceSymbolsForResult(converter, caller,
2513+
callContext.symMap);
2514+
2515+
// Evaluate the result length expression.
2516+
mlir::Type idxTy = builder.getIndexType();
23882517
auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value {
23892518
mlir::Value convertExpr = builder.createConvert(
23902519
loc, idxTy,

flang/test/Lower/HLFIR/elemental-array-ops.f90

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -177,13 +177,8 @@ end subroutine char_return
177177
! CHECK: ^bb0(%[[VAL_18:.*]]: index):
178178
! CHECK: %[[VAL_19:.*]] = hlfir.designate %[[VAL_12]]#0 (%[[VAL_18]]) typeparams %[[VAL_11]] : (!fir.box<!fir.array<?x!fir.char<1,3>>>, index, index) -> !fir.ref<!fir.char<1,3>>
179179
! CHECK: %[[VAL_20:.*]] = fir.emboxchar %[[VAL_19]], %[[VAL_11]] : (!fir.ref<!fir.char<1,3>>, index) -> !fir.boxchar<1>
180-
! CHECK: %[[VAL_21:.*]] = arith.constant 3 : i64
181-
! CHECK: %[[VAL_22:.*]] = fir.convert %[[VAL_21]] : (i64) -> index
182-
! CHECK: %[[VAL_23:.*]] = arith.constant 0 : index
183-
! CHECK: %[[VAL_24:.*]] = arith.cmpi sgt, %[[VAL_22]], %[[VAL_23]] : index
184-
! CHECK: %[[VAL_25:.*]] = arith.select %[[VAL_24]], %[[VAL_22]], %[[VAL_23]] : index
185-
! CHECK: %[[VAL_27:.*]] = fir.call @_QPcallee(%[[VAL_2]], %[[VAL_25]], %[[VAL_20]]) proc_attrs<elemental, pure> fastmath<contract> : (!fir.ref<!fir.char<1,3>>, index, !fir.boxchar<1>) -> !fir.boxchar<1>
186-
! CHECK: %[[VAL_28:.*]]:2 = hlfir.declare %[[VAL_2]] typeparams %[[VAL_25]] {uniq_name = ".tmp.func_result"} : (!fir.ref<!fir.char<1,3>>, index) -> (!fir.ref<!fir.char<1,3>>, !fir.ref<!fir.char<1,3>>)
180+
! CHECK: %[[VAL_27:.*]] = fir.call @_QPcallee(%[[VAL_2]], %[[VAL_16]], %[[VAL_20]]) proc_attrs<elemental, pure> fastmath<contract> : (!fir.ref<!fir.char<1,3>>, index, !fir.boxchar<1>) -> !fir.boxchar<1>
181+
! CHECK: %[[VAL_28:.*]]:2 = hlfir.declare %[[VAL_2]] typeparams %[[VAL_16]] {uniq_name = ".tmp.func_result"} : (!fir.ref<!fir.char<1,3>>, index) -> (!fir.ref<!fir.char<1,3>>, !fir.ref<!fir.char<1,3>>)
187182
! CHECK: %[[MustFree:.*]] = arith.constant false
188183
! CHECK: %[[ResultTemp:.*]] = hlfir.as_expr %[[VAL_28]]#0 move %[[MustFree]] : (!fir.ref<!fir.char<1,3>>, i1) -> !hlfir.expr<!fir.char<1,3>>
189184
! CHECK: hlfir.yield_element %[[ResultTemp]] : !hlfir.expr<!fir.char<1,3>>

flang/test/Lower/HLFIR/elemental-result-length.f90

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module m1
44
contains
55
elemental function fct1(a, b) result(t)
66
character(*), intent(in) :: a, b
7-
character(len(a) + len(b)) :: t
7+
character(len(a, kind=8) + len(b,kind=8)) :: t
88
t = a // b
99
end function
1010

@@ -27,10 +27,10 @@ subroutine sub2(a,b,c)
2727
! 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,?>>)
2828
! CHECK: %[[UNBOX_B:.*]]:2 = fir.unboxchar %[[B]]#0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
2929
! 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,?>>)
30-
! CHECK: %[[LEN_A:.*]] = fir.convert %[[UNBOX_A]]#1 : (index) -> i32
31-
! CHECK: %[[LEN_B:.*]] = fir.convert %[[UNBOX_B]]#1 : (index) -> i32
32-
! CHECK: %[[LEN_LEN:.*]] = arith.addi %[[LEN_A]], %[[LEN_B]] : i32
33-
! CHECK: %[[LEN_LEN_IDX:.*]] = fir.convert %[[LEN_LEN]] : (i32) -> index
30+
! CHECK: %[[LEN_A:.*]] = fir.convert %[[UNBOX_A]]#1 : (index) -> i64
31+
! CHECK: %[[LEN_B:.*]] = fir.convert %[[UNBOX_B]]#1 : (index) -> i64
32+
! CHECK: %[[LEN_LEN:.*]] = arith.addi %[[LEN_A]], %[[LEN_B]] : i64
33+
! CHECK: %[[LEN_LEN_IDX:.*]] = fir.convert %[[LEN_LEN]] : (i64) -> index
3434
! CHECK: %[[CMPI:.*]] = arith.cmpi sgt, %[[LEN_LEN_IDX]], %c0{{.*}} : index
3535
! CHECK: %[[RES_LENGTH:.*]] = arith.select %[[CMPI]], %[[LEN_LEN_IDX]], %c0{{.*}} : index
3636
! CHECK: %[[RES:.*]] = fir.alloca !fir.char<1,?>(%[[RES_LENGTH]] : index) {bindc_name = ".result"}
@@ -50,12 +50,12 @@ subroutine sub4(a,b,c)
5050
! 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,?>>>)
5151
! CHECK: %[[LEN_A:.*]] = fir.box_elesize %[[A]]#1 : (!fir.box<!fir.array<?x!fir.char<1,?>>>) -> index
5252
! CHECK: %[[LEN_B:.*]] = fir.box_elesize %[[B]]#1 : (!fir.box<!fir.array<?x!fir.char<1,?>>>) -> index
53-
! CHECK: %[[LEN_A_I32:.*]] = fir.convert %[[LEN_A]] : (index) -> i32
54-
! CHECK: %[[LEN_B_I32:.*]] = fir.convert %[[LEN_B]] : (index) -> i32
55-
! CHECK: %[[LEN_LEN:.*]] = arith.addi %[[LEN_A_I32]], %[[LEN_B_I32]] : i32
56-
! CHECK: %[[LEN_LEN_IDX:.*]] = fir.convert %[[LEN_LEN]] : (i32) -> index
53+
! CHECK: %[[LEN_A_I32:.*]] = fir.convert %[[LEN_A]] : (index) -> i64
54+
! CHECK: %[[LEN_B_I32:.*]] = fir.convert %[[LEN_B]] : (index) -> i64
55+
! CHECK: %[[LEN_LEN:.*]] = arith.addi %[[LEN_A_I32]], %[[LEN_B_I32]] : i64
56+
! CHECK: %[[LEN_LEN_IDX:.*]] = fir.convert %[[LEN_LEN]] : (i64) -> index
5757
! CHECK: %[[CMPI:.*]] = arith.cmpi sgt, %[[LEN_LEN_IDX]], %c0{{.*}} : index
58-
! CHECK: %[[LENGTH:.*]] = arith.select %[[CMPI]], %17, %c0{{.*}} : index
58+
! CHECK: %[[LENGTH:.*]] = arith.select %[[CMPI]], %[[LEN_LEN_IDX]], %c0{{.*}} : index
5959
! CHECK: %{{.*}} = hlfir.elemental %{{.*}} typeparams %[[LENGTH]] unordered : (!fir.shape<1>, index) -> !hlfir.expr<?x!fir.char<1,?>>
6060

6161
end module

0 commit comments

Comments
 (0)