[flang-commits] [flang] ebae4cc - [flang][hlfir] Fixed array constructor lowering.

Slava Zakharin via flang-commits flang-commits at lists.llvm.org
Fri May 5 10:09:28 PDT 2023


Author: Slava Zakharin
Date: 2023-05-05T10:09:22-07:00
New Revision: ebae4cc7cbe26cad2785106b0fc15a71a4b682ab

URL: https://github.com/llvm/llvm-project/commit/ebae4cc7cbe26cad2785106b0fc15a71a4b682ab
DIFF: https://github.com/llvm/llvm-project/commit/ebae4cc7cbe26cad2785106b0fc15a71a4b682ab.diff

LOG: [flang][hlfir] Fixed array constructor lowering.

First issue is that the clean-ups were generated after the yield_element
operation that must be the terminator. Second issue is that codegen for
elemenal operation was not working properly with nested elemental ops.

Differential Revision: https://reviews.llvm.org/D149921

Added: 
    flang/test/HLFIR/elemental-codegen-nested.fir
    flang/test/Lower/HLFIR/array-ctor-as-elemental-nested.f90

Modified: 
    flang/lib/Lower/ConvertArrayConstructor.cpp
    flang/lib/Optimizer/Builder/HLFIRTools.cpp
    flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp

Removed: 
    


################################################################################
diff  --git a/flang/lib/Lower/ConvertArrayConstructor.cpp b/flang/lib/Lower/ConvertArrayConstructor.cpp
index d5df432c4f257..24f49c241c57d 100644
--- 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 <typename CounterType>
-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 @@ class InlinedTempStrategyImpl {
   /// 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<mlir::Value> 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<mlir::Value, 1> extents{extent};
@@ -224,15 +259,17 @@ using InlinedTempStrategy = InlinedTempStrategyImpl<InMemoryCounter>;
 /// 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<mlir::Value> 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 @@ class AsElementalStrategy {
     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<hlfir::YieldElementOp>(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 AsElementalStrategy {
 
 /// 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 @@ class RuntimeTempStrategy {
   /// 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<mlir::Value> extent,
                       llvm::ArrayRef<mlir::Value> 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 @@ class ArrayCtorLoweringStrategy {
         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<InlinedTempStrategy, LooplessInlinedTempStrategy,
                AsElementalStrategy, RuntimeTempStrategy>
@@ -694,20 +757,22 @@ static ArrayCtorLoweringStrategy selectArrayCtorLoweringStrategy(
   if (!extent || needToEvaluateOneExprToGetLengthParameters ||
       analysis.anyArrayExpr || declaredType.getEleTy().isa<fir::RecordType>())
     return RuntimeTempStrategy(
-        loc, builder, declaredType,
+        loc, builder, stmtCtx, symMap, declaredType,
         extent ? std::optional<mlir::Value>(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 @@ static void genAcValue(mlir::Location loc,
   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 @@ static void genAcValue(mlir::Location loc,
         },
         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
index 1c2e3cfffd111..30bae873a5eaa 100644
--- a/flang/lib/Optimizer/Builder/HLFIRTools.cpp
+++ b/flang/lib/Optimizer/Builder/HLFIRTools.cpp
@@ -744,6 +744,9 @@ hlfir::genElementalOp(mlir::Location loc, fir::FirOpBuilder &builder,
   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
index 21fe2d9f89372..8d364eafb9391 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp
@@ -489,7 +489,13 @@ struct ElementalOpConversion
     : public mlir::OpConversionPattern<hlfir::ElementalOp> {
   using mlir::OpConversionPattern<hlfir::ElementalOp>::OpConversionPattern;
   explicit ElementalOpConversion(mlir::MLIRContext *ctx)
-      : mlir::OpConversionPattern<hlfir::ElementalOp>{ctx} {}
+      : mlir::OpConversionPattern<hlfir::ElementalOp>{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 @@ class BufferizeHLFIR : public hlfir::impl::BufferizeHLFIRBase<BufferizeHLFIR> {
                     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::ApplyOp, hlfir::AssociateOp, hlfir::ElementalOp,
-                        hlfir::EndAssociateOp, hlfir::SetLengthOp,
-                        hlfir::YieldElementOp>();
+                        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
index 0000000000000..b43286f51141c
--- /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<f32> {fir.bindc_name = "pi"},
+// CHECK-SAME:                       %[[VAL_1:.*]]: !fir.ref<!fir.array<2xf32>> {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.array<2xf32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<2xf32>>, !fir.ref<!fir.array<2xf32>>)
+// 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<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+// 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<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+// CHECK:           %[[VAL_9:.*]] = fir.address_of(@_QFtestECn) : !fir.ref<i32>
+// CHECK:           %[[VAL_10:.*]]:2 = hlfir.declare %[[VAL_9]] {fortran_attrs = #fir.var_attrs<parameter>, uniq_name = "_QFtestECn"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+// CHECK:           %[[VAL_11:.*]]:2 = hlfir.declare %[[VAL_0]] {uniq_name = "_QFtestEpi"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
+// 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.array<2xf32>>, !fir.shape<1>) -> (!fir.heap<!fir.array<2xf32>>, !fir.heap<!fir.array<2xf32>>)
+// 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.array<2xf32>>, !fir.shape<1>) -> (!fir.heap<!fir.array<2xf32>>, !fir.heap<!fir.array<2xf32>>)
+// 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<f32>
+// CHECK:               %[[VAL_25:.*]] = hlfir.designate %[[VAL_20]]#0 (%[[VAL_23]])  : (!fir.heap<!fir.array<2xf32>>, index) -> !fir.ref<f32>
+// CHECK:               hlfir.assign %[[VAL_24]] to %[[VAL_25]] : f32, !fir.ref<f32>
+// CHECK:             }
+// CHECK:             %[[VAL_26:.*]] = fir.undefined tuple<!fir.heap<!fir.array<2xf32>>, i1>
+// CHECK:             %[[VAL_27:.*]] = fir.insert_value %[[VAL_26]], %[[VAL_21]], [1 : index] : (tuple<!fir.heap<!fir.array<2xf32>>, i1>, i1) -> tuple<!fir.heap<!fir.array<2xf32>>, i1>
+// CHECK:             %[[VAL_28:.*]] = fir.insert_value %[[VAL_27]], %[[VAL_20]]#0, [0 : index] : (tuple<!fir.heap<!fir.array<2xf32>>, i1>, !fir.heap<!fir.array<2xf32>>) -> tuple<!fir.heap<!fir.array<2xf32>>, i1>
+// CHECK:             %[[VAL_29:.*]] = fir.convert %[[VAL_20]]#0 : (!fir.heap<!fir.array<2xf32>>) -> !fir.ref<!fir.array<2xf32>>
+// CHECK:             %[[VAL_30:.*]] = fir.convert %[[VAL_20]]#1 : (!fir.heap<!fir.array<2xf32>>) -> !fir.ref<!fir.array<2xf32>>
+// CHECK:             %[[VAL_31:.*]] = fir.embox %[[VAL_29]](%[[VAL_18]]) : (!fir.ref<!fir.array<2xf32>>, !fir.shape<1>) -> !fir.box<!fir.array<2xf32>>
+// CHECK:             %[[VAL_32:.*]] = fir.convert %[[VAL_31]] : (!fir.box<!fir.array<2xf32>>) -> !fir.box<!fir.array<?xf32>>
+// CHECK:             %[[VAL_33:.*]] = fir.call @_QPfoo(%[[VAL_32]]) fastmath<contract> : (!fir.box<!fir.array<?xf32>>) -> f32
+// CHECK:             %[[VAL_34:.*]] = fir.convert %[[VAL_30]] : (!fir.ref<!fir.array<2xf32>>) -> !fir.heap<!fir.array<2xf32>>
+// CHECK:             fir.freemem %[[VAL_34]] : !fir.heap<!fir.array<2xf32>>
+// CHECK:             %[[VAL_35:.*]] = hlfir.designate %[[VAL_14]]#0 (%[[VAL_17]])  : (!fir.heap<!fir.array<2xf32>>, index) -> !fir.ref<f32>
+// CHECK:             hlfir.assign %[[VAL_33]] to %[[VAL_35]] : f32, !fir.ref<f32>
+// CHECK:           }
+// CHECK:           %[[VAL_36:.*]] = fir.undefined tuple<!fir.heap<!fir.array<2xf32>>, i1>
+// CHECK:           %[[VAL_37:.*]] = fir.insert_value %[[VAL_36]], %[[VAL_15]], [1 : index] : (tuple<!fir.heap<!fir.array<2xf32>>, i1>, i1) -> tuple<!fir.heap<!fir.array<2xf32>>, i1>
+// CHECK:           %[[VAL_38:.*]] = fir.insert_value %[[VAL_37]], %[[VAL_14]]#0, [0 : index] : (tuple<!fir.heap<!fir.array<2xf32>>, i1>, !fir.heap<!fir.array<2xf32>>) -> tuple<!fir.heap<!fir.array<2xf32>>, i1>
+// CHECK:           hlfir.assign %[[VAL_14]]#0 to %[[VAL_4]]#0 : !fir.heap<!fir.array<2xf32>>, !fir.ref<!fir.array<2xf32>>
+// CHECK:           fir.freemem %[[VAL_14]]#1 : !fir.heap<!fir.array<2xf32>>
+// CHECK:           return
+// CHECK:         }
+func.func @_QPtest(%arg0: !fir.ref<f32> {fir.bindc_name = "pi"}, %arg1: !fir.ref<!fir.array<2xf32>> {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.array<2xf32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<2xf32>>, !fir.ref<!fir.array<2xf32>>)
+  %2 = fir.alloca i32 {bindc_name = "k", uniq_name = "_QFtestEk"}
+  %3:2 = hlfir.declare %2 {uniq_name = "_QFtestEk"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+  %4 = fir.alloca i32 {bindc_name = "l", uniq_name = "_QFtestEl"}
+  %5:2 = hlfir.declare %4 {uniq_name = "_QFtestEl"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+  %6 = fir.address_of(@_QFtestECn) : !fir.ref<i32>
+  %7:2 = hlfir.declare %6 {fortran_attrs = #fir.var_attrs<parameter>, uniq_name = "_QFtestECn"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+  %8:2 = hlfir.declare %arg0 {uniq_name = "_QFtestEpi"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
+  %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<f32>
+      hlfir.yield_element %17 : f32
+    }
+    %13:3 = hlfir.associate %12(%11) {uniq_name = "adapt.valuebyref"} : (!hlfir.expr<2xf32>, !fir.shape<1>) -> (!fir.ref<!fir.array<2xf32>>, !fir.ref<!fir.array<2xf32>>, i1)
+    %14 = fir.embox %13#0(%11) : (!fir.ref<!fir.array<2xf32>>, !fir.shape<1>) -> !fir.box<!fir.array<2xf32>>
+    %15 = fir.convert %14 : (!fir.box<!fir.array<2xf32>>) -> !fir.box<!fir.array<?xf32>>
+    %16 = fir.call @_QPfoo(%15) fastmath<contract> : (!fir.box<!fir.array<?xf32>>) -> f32
+    hlfir.end_associate %13#1, %13#2 : !fir.ref<!fir.array<2xf32>>, i1
+    hlfir.destroy %12 : !hlfir.expr<2xf32>
+    hlfir.yield_element %16 : f32
+  }
+  hlfir.assign %10 to %1#0 : !hlfir.expr<2xf32>, !fir.ref<!fir.array<2xf32>>
+  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<!fir.array<?xf32>>) -> 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
index 0000000000000..6cfdb20aa43ce
--- /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<f32> {fir.bindc_name = "pi"},
+! CHECK-SAME:                       %[[VAL_1:.*]]: !fir.ref<!fir.array<2xf32>> {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.array<2xf32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<2xf32>>, !fir.ref<!fir.array<2xf32>>)
+! 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<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! 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<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK:           %[[VAL_9:.*]] = fir.address_of(@_QFtestECn) : !fir.ref<i32>
+! CHECK:           %[[VAL_10:.*]]:2 = hlfir.declare %[[VAL_9]] {fortran_attrs = #fir.var_attrs<parameter>, uniq_name = "_QFtestECn"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK:           %[[VAL_11:.*]]:2 = hlfir.declare %[[VAL_0]] {uniq_name = "_QFtestEpi"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
+! 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<f32>
+! 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.array<2xf32>>, !fir.ref<!fir.array<2xf32>>, i1)
+! CHECK:             %[[VAL_23:.*]] = fir.embox %[[VAL_21]]#0(%[[VAL_17]]) : (!fir.ref<!fir.array<2xf32>>, !fir.shape<1>) -> !fir.box<!fir.array<2xf32>>
+! CHECK:             %[[VAL_24:.*]] = fir.convert %[[VAL_23]] : (!fir.box<!fir.array<2xf32>>) -> !fir.box<!fir.array<?xf32>>
+! CHECK:             %[[VAL_25:.*]] = fir.call @_QPfoo(%[[VAL_24]]) fastmath<contract> : (!fir.box<!fir.array<?xf32>>) -> f32
+! CHECK:             hlfir.end_associate %[[VAL_21]]#1, %[[VAL_21]]#2 : !fir.ref<!fir.array<2xf32>>, 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<!fir.array<2xf32>>
+! 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


        


More information about the flang-commits mailing list