@@ -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+
290300std::pair<Fortran::lower::LoweredResult, bool >
291301Fortran::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+
23332394class ElementalUserCallBuilder
23342395 : public ElementalCallBuilder<ElementalUserCallBuilder> {
23352396public:
@@ -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,
0 commit comments