diff --git a/flang/lib/Lower/ConvertArrayConstructor.cpp b/flang/lib/Lower/ConvertArrayConstructor.cpp --- a/flang/lib/Lower/ConvertArrayConstructor.cpp +++ b/flang/lib/Lower/ConvertArrayConstructor.cpp @@ -56,23 +56,55 @@ // strategy.pushValue(ac-value); // } else if (ac-value is implied-do) { // strategy.startImpliedDo(lower, upper, stride); +// strategy.startImpliedDoScope(); // // lower nested values +// ... +// strategy.endImpliedDoScope(); // } // result = strategy.finishArrayCtorLowering(); // ``` //===----------------------------------------------------------------------===// // Definition of the lowering strategies. Each lowering strategy is defined -// as a class that implements "pushValue", "startImpliedDo", and -// "finishArrayCtorLowering". +// as a class that implements "pushValue", "startImpliedDo" and +// "finishArrayCtorLowering". A strategy may optionally override +// "startImpliedDoScope" and "endImpliedDoScope" virtual methods +// of its base class StrategyBase. //===----------------------------------------------------------------------===// namespace { +/// Class provides common implementation of scope push/pop methods +/// that update StatementContext scopes and SymMap bindings. +/// They might be overridden by the lowering strategies, e.g. +/// see AsElementalStrategy. +class StrategyBase { +public: + StrategyBase(Fortran::lower::StatementContext &stmtCtx, + Fortran::lower::SymMap &symMap) + : stmtCtx{stmtCtx}, symMap{symMap} {}; + virtual ~StrategyBase() = default; + + virtual void startImpliedDoScope(llvm::StringRef doName, + mlir::Value indexValue) { + symMap.pushImpliedDoBinding(doName, indexValue); + stmtCtx.pushScope(); + } + + virtual void endImpliedDoScope() { + stmtCtx.finalizeAndPop(); + symMap.popImpliedDoBinding(); + } + +protected: + Fortran::lower::StatementContext &stmtCtx; + Fortran::lower::SymMap &symMap; +}; + /// Class that implements the "inlined temp strategy" to lower array /// constructors. It must be further provided a CounterType class to specify how /// the current ac-value insertion position is tracked. template -class InlinedTempStrategyImpl { +class InlinedTempStrategyImpl : public StrategyBase { /// Name that will be given to the temporary allocation and hlfir.declare in /// the IR. static constexpr char tempName[] = ".tmp.arrayctor"; @@ -81,9 +113,12 @@ /// Start lowering an array constructor according to the inline strategy. /// The temporary is created right away. InlinedTempStrategyImpl(mlir::Location loc, fir::FirOpBuilder &builder, + Fortran::lower::StatementContext &stmtCtx, + Fortran::lower::SymMap &symMap, fir::SequenceType declaredType, mlir::Value extent, llvm::ArrayRef lengths) - : one{builder.createIntegerConstant(loc, builder.getIndexType(), 1)}, + : StrategyBase{stmtCtx, symMap}, + one{builder.createIntegerConstant(loc, builder.getIndexType(), 1)}, counter{loc, builder, one} { // Allocate the temporary storage. llvm::SmallVector extents{extent}; @@ -224,15 +259,17 @@ /// implied-do nest and wrapped in an hlfir.reshape to a rank 1 array. But this /// op does not exist yet, so this is left for the future if it appears /// profitable. -class AsElementalStrategy { +class AsElementalStrategy : public StrategyBase { public: /// The constructor only gathers the operands to create the hlfir.elemental. AsElementalStrategy(mlir::Location loc, fir::FirOpBuilder &builder, + Fortran::lower::StatementContext &stmtCtx, + Fortran::lower::SymMap &symMap, fir::SequenceType declaredType, mlir::Value extent, llvm::ArrayRef lengths) - : shape{builder.genShape(loc, {extent})}, - lengthParams{lengths.begin(), lengths.end()}, exprType{getExprType( - declaredType)} {} + : StrategyBase{stmtCtx, symMap}, shape{builder.genShape(loc, {extent})}, + lengthParams{lengths.begin(), lengths.end()}, + exprType{getExprType(declaredType)} {} static hlfir::ExprType getExprType(fir::SequenceType declaredType) { // Note: 7.8 point 4: the dynamic type of an array constructor is its static @@ -271,9 +308,19 @@ if (fir::isa_trivial(elementResult.getType())) elementResult = builder.createConvert(loc, exprType.getElementType(), elementResult); + + // The clean-ups associated with the implied-do body operations + // must be initiated before the YieldElementOp, so we have to pop the scope + // right now. + stmtCtx.finalizeAndPop(); + builder.create(loc, elementResult); } + // Override the default, because the context scope must be popped in + // pushValue(). + virtual void endImpliedDoScope() override { symMap.popImpliedDoBinding(); } + /// Return the created hlfir.elemental. hlfir::Entity finishArrayCtorLowering(mlir::Location loc, fir::FirOpBuilder &builder) { @@ -289,7 +336,7 @@ /// Class that implements the "runtime temp strategy" to lower array /// constructors. -class RuntimeTempStrategy { +class RuntimeTempStrategy : public StrategyBase { /// Name that will be given to the temporary allocation and hlfir.declare in /// the IR. static constexpr char tempName[] = ".tmp.arrayctor"; @@ -305,11 +352,14 @@ /// constructor, if they could be precomputed. \p missingLengthParameters is /// set to true if the length parameters could not be precomputed. RuntimeTempStrategy(mlir::Location loc, fir::FirOpBuilder &builder, + Fortran::lower::StatementContext &stmtCtx, + Fortran::lower::SymMap &symMap, fir::SequenceType declaredType, std::optional extent, llvm::ArrayRef lengths, bool missingLengthParameters) - : arrayConstructorElementType{declaredType.getEleTy()} { + : StrategyBase{stmtCtx, symMap}, + arrayConstructorElementType{declaredType.getEleTy()} { mlir::Type heapType = fir::HeapType::get(declaredType); mlir::Type boxType = fir::BoxType::get(heapType); allocatableTemp = builder.createTemporary(loc, boxType, tempName); @@ -463,6 +513,19 @@ implVariant); } + void startImpliedDoScope(llvm::StringRef doName, mlir::Value indexValue) { + std::visit( + [&](auto &impl) { + return impl.startImpliedDoScope(doName, indexValue); + }, + implVariant); + } + + void endImpliedDoScope() { + std::visit([&](auto &impl) { return impl.endImpliedDoScope(); }, + implVariant); + } + private: std::variant @@ -694,20 +757,22 @@ if (!extent || needToEvaluateOneExprToGetLengthParameters || analysis.anyArrayExpr || declaredType.getEleTy().isa()) return RuntimeTempStrategy( - loc, builder, declaredType, + loc, builder, stmtCtx, symMap, declaredType, extent ? std::optional(extent) : std::nullopt, lengths, needToEvaluateOneExprToGetLengthParameters); // Note: array constructors containing impure ac-value expr are currently not // rewritten to hlfir.elemental because impure expressions should be evaluated // in order, and hlfir.elemental currently misses a way to indicate that. if (analysis.isSingleImpliedDoWithOneScalarPureExpr()) - return AsElementalStrategy(loc, builder, declaredType, extent, lengths); + return AsElementalStrategy(loc, builder, stmtCtx, symMap, declaredType, + extent, lengths); if (analysis.anyImpliedDo) - return InlinedTempStrategy(loc, builder, declaredType, extent, lengths); + return InlinedTempStrategy(loc, builder, stmtCtx, symMap, declaredType, + extent, lengths); - return LooplessInlinedTempStrategy(loc, builder, declaredType, extent, - lengths); + return LooplessInlinedTempStrategy(loc, builder, stmtCtx, symMap, + declaredType, extent, lengths); } /// Lower an ac-value expression \p expr and forward it to the selected @@ -747,9 +812,8 @@ mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint(); mlir::Value impliedDoIndexValue = arrayBuilder.startImpliedDo(loc, builder, lower, upper, stride); - symMap.pushImpliedDoBinding(toStringRef(impledDo.name()), - impliedDoIndexValue); - stmtCtx.pushScope(); + arrayBuilder.startImpliedDoScope(toStringRef(impledDo.name()), + impliedDoIndexValue); for (const auto &acValue : impledDo.values()) std::visit( @@ -758,8 +822,7 @@ }, acValue.u); - stmtCtx.finalizeAndPop(); - symMap.popImpliedDoBinding(); + arrayBuilder.endImpliedDoScope(); builder.restoreInsertionPoint(insertPt); } diff --git a/flang/lib/Optimizer/Builder/HLFIRTools.cpp b/flang/lib/Optimizer/Builder/HLFIRTools.cpp --- a/flang/lib/Optimizer/Builder/HLFIRTools.cpp +++ b/flang/lib/Optimizer/Builder/HLFIRTools.cpp @@ -744,6 +744,9 @@ return elementalOp; } +// TODO: we do not actually need to clone the YieldElementOp, +// because returning its getElementValue() operand should be enough +// for all callers of this function. hlfir::YieldElementOp hlfir::inlineElementalOp(mlir::Location loc, fir::FirOpBuilder &builder, hlfir::ElementalOp elemental, diff --git a/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp b/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp --- a/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp +++ b/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp @@ -489,7 +489,13 @@ : public mlir::OpConversionPattern { using mlir::OpConversionPattern::OpConversionPattern; explicit ElementalOpConversion(mlir::MLIRContext *ctx) - : mlir::OpConversionPattern{ctx} {} + : mlir::OpConversionPattern{ctx} { + // This pattern recursively converts nested ElementalOp's + // by cloning and then converting them, so we have to allow + // for recursive pattern application. The recursion is bounded + // by the nesting level of ElementalOp's. + setHasBoundedRewriteRecursion(); + } mlir::LogicalResult matchAndRewrite(hlfir::ElementalOp elemental, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const override { @@ -568,9 +574,14 @@ EndAssociateOpConversion, NoReassocOpConversion, SetLengthOpConversion, ShapeOfOpConversion>(context); mlir::ConversionTarget target(*context); + // Note that YieldElementOp is not marked as an illegal operation. + // It must be erased by its parent converter and there is no explicit + // conversion pattern to YieldElementOp itself. If any YieldElementOp + // survives this pass, the verifier will detect it because it has to be + // a child of ElementalOp and ElementalOp's are explicitly illegal. target.addIllegalOp(); + hlfir::EndAssociateOp, hlfir::SetLengthOp>(); + target.markUnknownOpDynamicallyLegal([](mlir::Operation *op) { return llvm::all_of( op->getResultTypes(), diff --git a/flang/test/HLFIR/elemental-codegen-nested.fir b/flang/test/HLFIR/elemental-codegen-nested.fir new file mode 100644 --- /dev/null +++ b/flang/test/HLFIR/elemental-codegen-nested.fir @@ -0,0 +1,89 @@ +// Test nested hlfir.elemental code generation +// RUN: fir-opt %s --bufferize-hlfir | FileCheck %s + +// CHECK-LABEL: func.func @_QPtest( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref {fir.bindc_name = "pi"}, +// CHECK-SAME: %[[VAL_1:.*]]: !fir.ref> {fir.bindc_name = "h1"}) { +// CHECK: %[[VAL_2:.*]] = arith.constant 2 : index +// CHECK: %[[VAL_3:.*]] = fir.shape %[[VAL_2]] : (index) -> !fir.shape<1> +// CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[VAL_1]](%[[VAL_3]]) {uniq_name = "_QFtestEh1"} : (!fir.ref>, !fir.shape<1>) -> (!fir.ref>, !fir.ref>) +// CHECK: %[[VAL_5:.*]] = fir.alloca i32 {bindc_name = "k", uniq_name = "_QFtestEk"} +// CHECK: %[[VAL_6:.*]]:2 = hlfir.declare %[[VAL_5]] {uniq_name = "_QFtestEk"} : (!fir.ref) -> (!fir.ref, !fir.ref) +// CHECK: %[[VAL_7:.*]] = fir.alloca i32 {bindc_name = "l", uniq_name = "_QFtestEl"} +// CHECK: %[[VAL_8:.*]]:2 = hlfir.declare %[[VAL_7]] {uniq_name = "_QFtestEl"} : (!fir.ref) -> (!fir.ref, !fir.ref) +// CHECK: %[[VAL_9:.*]] = fir.address_of(@_QFtestECn) : !fir.ref +// CHECK: %[[VAL_10:.*]]:2 = hlfir.declare %[[VAL_9]] {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestECn"} : (!fir.ref) -> (!fir.ref, !fir.ref) +// CHECK: %[[VAL_11:.*]]:2 = hlfir.declare %[[VAL_0]] {uniq_name = "_QFtestEpi"} : (!fir.ref) -> (!fir.ref, !fir.ref) +// CHECK: %[[VAL_12:.*]] = fir.shape %[[VAL_2]] : (index) -> !fir.shape<1> +// CHECK: %[[VAL_13:.*]] = fir.allocmem !fir.array<2xf32> {bindc_name = ".tmp.array", uniq_name = ""} +// CHECK: %[[VAL_14:.*]]:2 = hlfir.declare %[[VAL_13]](%[[VAL_12]]) {uniq_name = ".tmp.array"} : (!fir.heap>, !fir.shape<1>) -> (!fir.heap>, !fir.heap>) +// CHECK: %[[VAL_15:.*]] = arith.constant true +// CHECK: %[[VAL_16:.*]] = arith.constant 1 : index +// CHECK: fir.do_loop %[[VAL_17:.*]] = %[[VAL_16]] to %[[VAL_2]] step %[[VAL_16]] { +// CHECK: %[[VAL_18:.*]] = fir.shape %[[VAL_2]] : (index) -> !fir.shape<1> +// CHECK: %[[VAL_19:.*]] = fir.allocmem !fir.array<2xf32> {bindc_name = ".tmp.array", uniq_name = ""} +// CHECK: %[[VAL_20:.*]]:2 = hlfir.declare %[[VAL_19]](%[[VAL_18]]) {uniq_name = ".tmp.array"} : (!fir.heap>, !fir.shape<1>) -> (!fir.heap>, !fir.heap>) +// CHECK: %[[VAL_21:.*]] = arith.constant true +// CHECK: %[[VAL_22:.*]] = arith.constant 1 : index +// CHECK: fir.do_loop %[[VAL_23:.*]] = %[[VAL_22]] to %[[VAL_2]] step %[[VAL_22]] { +// CHECK: %[[VAL_24:.*]] = fir.load %[[VAL_11]]#0 : !fir.ref +// CHECK: %[[VAL_25:.*]] = hlfir.designate %[[VAL_20]]#0 (%[[VAL_23]]) : (!fir.heap>, index) -> !fir.ref +// CHECK: hlfir.assign %[[VAL_24]] to %[[VAL_25]] : f32, !fir.ref +// CHECK: } +// CHECK: %[[VAL_26:.*]] = fir.undefined tuple>, i1> +// CHECK: %[[VAL_27:.*]] = fir.insert_value %[[VAL_26]], %[[VAL_21]], [1 : index] : (tuple>, i1>, i1) -> tuple>, i1> +// CHECK: %[[VAL_28:.*]] = fir.insert_value %[[VAL_27]], %[[VAL_20]]#0, [0 : index] : (tuple>, i1>, !fir.heap>) -> tuple>, i1> +// CHECK: %[[VAL_29:.*]] = fir.convert %[[VAL_20]]#0 : (!fir.heap>) -> !fir.ref> +// CHECK: %[[VAL_30:.*]] = fir.convert %[[VAL_20]]#1 : (!fir.heap>) -> !fir.ref> +// CHECK: %[[VAL_31:.*]] = fir.embox %[[VAL_29]](%[[VAL_18]]) : (!fir.ref>, !fir.shape<1>) -> !fir.box> +// CHECK: %[[VAL_32:.*]] = fir.convert %[[VAL_31]] : (!fir.box>) -> !fir.box> +// CHECK: %[[VAL_33:.*]] = fir.call @_QPfoo(%[[VAL_32]]) fastmath : (!fir.box>) -> f32 +// CHECK: %[[VAL_34:.*]] = fir.convert %[[VAL_30]] : (!fir.ref>) -> !fir.heap> +// CHECK: fir.freemem %[[VAL_34]] : !fir.heap> +// CHECK: %[[VAL_35:.*]] = hlfir.designate %[[VAL_14]]#0 (%[[VAL_17]]) : (!fir.heap>, index) -> !fir.ref +// CHECK: hlfir.assign %[[VAL_33]] to %[[VAL_35]] : f32, !fir.ref +// CHECK: } +// CHECK: %[[VAL_36:.*]] = fir.undefined tuple>, i1> +// CHECK: %[[VAL_37:.*]] = fir.insert_value %[[VAL_36]], %[[VAL_15]], [1 : index] : (tuple>, i1>, i1) -> tuple>, i1> +// CHECK: %[[VAL_38:.*]] = fir.insert_value %[[VAL_37]], %[[VAL_14]]#0, [0 : index] : (tuple>, i1>, !fir.heap>) -> tuple>, i1> +// CHECK: hlfir.assign %[[VAL_14]]#0 to %[[VAL_4]]#0 : !fir.heap>, !fir.ref> +// CHECK: fir.freemem %[[VAL_14]]#1 : !fir.heap> +// CHECK: return +// CHECK: } +func.func @_QPtest(%arg0: !fir.ref {fir.bindc_name = "pi"}, %arg1: !fir.ref> {fir.bindc_name = "h1"}) { + %c2 = arith.constant 2 : index + %0 = fir.shape %c2 : (index) -> !fir.shape<1> + %1:2 = hlfir.declare %arg1(%0) {uniq_name = "_QFtestEh1"} : (!fir.ref>, !fir.shape<1>) -> (!fir.ref>, !fir.ref>) + %2 = fir.alloca i32 {bindc_name = "k", uniq_name = "_QFtestEk"} + %3:2 = hlfir.declare %2 {uniq_name = "_QFtestEk"} : (!fir.ref) -> (!fir.ref, !fir.ref) + %4 = fir.alloca i32 {bindc_name = "l", uniq_name = "_QFtestEl"} + %5:2 = hlfir.declare %4 {uniq_name = "_QFtestEl"} : (!fir.ref) -> (!fir.ref, !fir.ref) + %6 = fir.address_of(@_QFtestECn) : !fir.ref + %7:2 = hlfir.declare %6 {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestECn"} : (!fir.ref) -> (!fir.ref, !fir.ref) + %8:2 = hlfir.declare %arg0 {uniq_name = "_QFtestEpi"} : (!fir.ref) -> (!fir.ref, !fir.ref) + %9 = fir.shape %c2 : (index) -> !fir.shape<1> + %10 = hlfir.elemental %9 : (!fir.shape<1>) -> !hlfir.expr<2xf32> { + ^bb0(%arg2: index): + %11 = fir.shape %c2 : (index) -> !fir.shape<1> + %12 = hlfir.elemental %11 : (!fir.shape<1>) -> !hlfir.expr<2xf32> { + ^bb0(%arg3: index): + %17 = fir.load %8#0 : !fir.ref + hlfir.yield_element %17 : f32 + } + %13:3 = hlfir.associate %12(%11) {uniq_name = "adapt.valuebyref"} : (!hlfir.expr<2xf32>, !fir.shape<1>) -> (!fir.ref>, !fir.ref>, i1) + %14 = fir.embox %13#0(%11) : (!fir.ref>, !fir.shape<1>) -> !fir.box> + %15 = fir.convert %14 : (!fir.box>) -> !fir.box> + %16 = fir.call @_QPfoo(%15) fastmath : (!fir.box>) -> f32 + hlfir.end_associate %13#1, %13#2 : !fir.ref>, i1 + hlfir.destroy %12 : !hlfir.expr<2xf32> + hlfir.yield_element %16 : f32 + } + hlfir.assign %10 to %1#0 : !hlfir.expr<2xf32>, !fir.ref> + hlfir.destroy %10 : !hlfir.expr<2xf32> + return +} +fir.global internal @_QFtestECn constant : i32 { + %c2_i32 = arith.constant 2 : i32 + fir.has_value %c2_i32 : i32 +} +func.func private @_QPfoo(!fir.box>) -> f32 diff --git a/flang/test/Lower/HLFIR/array-ctor-as-elemental-nested.f90 b/flang/test/Lower/HLFIR/array-ctor-as-elemental-nested.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Lower/HLFIR/array-ctor-as-elemental-nested.f90 @@ -0,0 +1,55 @@ +! Test lowering of nested array constructors as hlfir.elemental. +! RUN: bbc -emit-fir -hlfir -o - %s | FileCheck %s + +! hlfir.end_associate and hlfir.destroy used to be generated +! after hlfir.yield_element for the outermost hlfir.elemental. + +! CHECK-LABEL: func.func @_QPtest( +! CHECK-SAME: %[[VAL_0:.*]]: !fir.ref {fir.bindc_name = "pi"}, +! CHECK-SAME: %[[VAL_1:.*]]: !fir.ref> {fir.bindc_name = "h1"}) { +! CHECK: %[[VAL_2:.*]] = arith.constant 2 : index +! CHECK: %[[VAL_3:.*]] = fir.shape %[[VAL_2]] : (index) -> !fir.shape<1> +! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[VAL_1]](%[[VAL_3]]) {uniq_name = "_QFtestEh1"} : (!fir.ref>, !fir.shape<1>) -> (!fir.ref>, !fir.ref>) +! CHECK: %[[VAL_5:.*]] = fir.alloca i32 {bindc_name = "k", uniq_name = "_QFtestEk"} +! CHECK: %[[VAL_6:.*]]:2 = hlfir.declare %[[VAL_5]] {uniq_name = "_QFtestEk"} : (!fir.ref) -> (!fir.ref, !fir.ref) +! CHECK: %[[VAL_7:.*]] = fir.alloca i32 {bindc_name = "l", uniq_name = "_QFtestEl"} +! CHECK: %[[VAL_8:.*]]:2 = hlfir.declare %[[VAL_7]] {uniq_name = "_QFtestEl"} : (!fir.ref) -> (!fir.ref, !fir.ref) +! CHECK: %[[VAL_9:.*]] = fir.address_of(@_QFtestECn) : !fir.ref +! CHECK: %[[VAL_10:.*]]:2 = hlfir.declare %[[VAL_9]] {fortran_attrs = #fir.var_attrs, uniq_name = "_QFtestECn"} : (!fir.ref) -> (!fir.ref, !fir.ref) +! CHECK: %[[VAL_11:.*]]:2 = hlfir.declare %[[VAL_0]] {uniq_name = "_QFtestEpi"} : (!fir.ref) -> (!fir.ref, !fir.ref) +! CHECK: %[[VAL_12:.*]] = arith.constant 2 : index +! CHECK: %[[VAL_13:.*]] = fir.shape %[[VAL_12]] : (index) -> !fir.shape<1> +! CHECK: %[[VAL_14:.*]] = hlfir.elemental %[[VAL_13]] : (!fir.shape<1>) -> !hlfir.expr<2xf32> { +! CHECK: ^bb0(%[[VAL_15:.*]]: index): +! CHECK: %[[VAL_16:.*]] = arith.constant 2 : index +! CHECK: %[[VAL_17:.*]] = fir.shape %[[VAL_16]] : (index) -> !fir.shape<1> +! CHECK: %[[VAL_18:.*]] = hlfir.elemental %[[VAL_17]] : (!fir.shape<1>) -> !hlfir.expr<2xf32> { +! CHECK: ^bb0(%[[VAL_19:.*]]: index): +! CHECK: %[[VAL_20:.*]] = fir.load %[[VAL_11]]#0 : !fir.ref +! CHECK: hlfir.yield_element %[[VAL_20]] : f32 +! CHECK: } +! CHECK: %[[VAL_21:.*]]:3 = hlfir.associate %[[VAL_22:.*]](%[[VAL_17]]) {uniq_name = "adapt.valuebyref"} : (!hlfir.expr<2xf32>, !fir.shape<1>) -> (!fir.ref>, !fir.ref>, i1) +! CHECK: %[[VAL_23:.*]] = fir.embox %[[VAL_21]]#0(%[[VAL_17]]) : (!fir.ref>, !fir.shape<1>) -> !fir.box> +! CHECK: %[[VAL_24:.*]] = fir.convert %[[VAL_23]] : (!fir.box>) -> !fir.box> +! CHECK: %[[VAL_25:.*]] = fir.call @_QPfoo(%[[VAL_24]]) fastmath : (!fir.box>) -> f32 +! CHECK: hlfir.end_associate %[[VAL_21]]#1, %[[VAL_21]]#2 : !fir.ref>, i1 +! CHECK: hlfir.destroy %[[VAL_22]] : !hlfir.expr<2xf32> +! CHECK: hlfir.yield_element %[[VAL_25]] : f32 +! CHECK: } +! CHECK: hlfir.assign %[[VAL_26:.*]] to %[[VAL_4]]#0 : !hlfir.expr<2xf32>, !fir.ref> +! CHECK: hlfir.destroy %[[VAL_26]] : !hlfir.expr<2xf32> +! CHECK: return +! CHECK: } +subroutine test(pi,h1) + implicit none + integer, parameter :: N = 2 + interface + pure real function foo(x) + real, intent(in) :: x(:) + end function foo + end interface + real h1(1:N) + integer k, l + real pi + h1 = (/(foo((/(pi,l=1,N)/)),k=1,N)/) +end subroutine test