@@ -287,6 +287,16 @@ static void remapActualToDummyDescriptors(
287
287
}
288
288
}
289
289
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
+
290
300
std::pair<Fortran::lower::LoweredResult, bool >
291
301
Fortran::lower::genCallOpAndResult (
292
302
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
@@ -296,7 +306,13 @@ Fortran::lower::genCallOpAndResult(
296
306
fir::FirOpBuilder &builder = converter.getFirOpBuilder ();
297
307
using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
298
308
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.
300
316
symMap.pushScope ();
301
317
mustPopSymMap = true ;
302
318
Fortran::lower::mapCallInterfaceSymbolsForResult (converter, caller, symMap);
@@ -340,7 +356,6 @@ Fortran::lower::genCallOpAndResult(
340
356
loc, idxTy, fir::getBase (converter.genExprValue (expr, stmtCtx)));
341
357
return fir::factory::genMaxWithZero (builder, loc, convertExpr);
342
358
};
343
- llvm::SmallVector<mlir::Value> resultLengths;
344
359
mlir::Value arrayResultShape;
345
360
hlfir::EvaluateInMemoryOp evaluateInMemory;
346
361
auto allocatedResult = [&]() -> std::optional<fir::ExtendedValue> {
@@ -355,11 +370,16 @@ Fortran::lower::genCallOpAndResult(
355
370
assert (!isAssumedSizeExtent && " result cannot be assumed-size" );
356
371
extents.emplace_back (lowerSpecExpr (e));
357
372
});
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
+ }
363
383
364
384
// Result length parameters should not be provided to box storage
365
385
// allocation and save_results, but they are still useful information to
@@ -2330,6 +2350,47 @@ class ElementalCallBuilder {
2330
2350
}
2331
2351
};
2332
2352
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
+
2333
2394
class ElementalUserCallBuilder
2334
2395
: public ElementalCallBuilder<ElementalUserCallBuilder> {
2335
2396
public:
@@ -2362,29 +2423,97 @@ class ElementalUserCallBuilder
2362
2423
mlir::Value computeDynamicCharacterResultLength (
2363
2424
Fortran::lower::PreparedActualArguments &loweredActuals,
2364
2425
CallContext &callContext) {
2426
+
2365
2427
fir::FirOpBuilder &builder = callContext.getBuilder ();
2366
2428
mlir::Location loc = callContext.loc ;
2367
2429
auto &converter = callContext.converter ;
2368
- mlir::Type idxTy = builder.getIndexType ();
2369
- llvm::SmallVector<CallCleanUp> callCleanUps;
2370
2430
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
+ }
2373
2443
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
+ }
2375
2474
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
+ }
2386
2504
}
2387
2505
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 ();
2388
2517
auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value {
2389
2518
mlir::Value convertExpr = builder.createConvert (
2390
2519
loc, idxTy,
0 commit comments