xref: /llvm-project/flang/lib/Lower/ConvertArrayConstructor.cpp (revision 6f8ef5ad2f35321257adbe353f86027bf5209023)
1ffde9f17SJean Perier //===- ConvertArrayConstructor.cpp -- Array Constructor ---------*- C++ -*-===//
2ffde9f17SJean Perier //
3ffde9f17SJean Perier // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4ffde9f17SJean Perier // See https://llvm.org/LICENSE.txt for license information.
5ffde9f17SJean Perier // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6ffde9f17SJean Perier //
7ffde9f17SJean Perier //===----------------------------------------------------------------------===//
8ffde9f17SJean Perier 
9ffde9f17SJean Perier #include "flang/Lower/ConvertArrayConstructor.h"
10ffde9f17SJean Perier #include "flang/Evaluate/expression.h"
11ffde9f17SJean Perier #include "flang/Lower/AbstractConverter.h"
12ffde9f17SJean Perier #include "flang/Lower/ConvertExprToHLFIR.h"
13ffde9f17SJean Perier #include "flang/Lower/ConvertType.h"
14ffde9f17SJean Perier #include "flang/Lower/StatementContext.h"
15ffde9f17SJean Perier #include "flang/Lower/SymbolMap.h"
16ffde9f17SJean Perier #include "flang/Optimizer/Builder/HLFIRTools.h"
179683a9c9SJean Perier #include "flang/Optimizer/Builder/Runtime/ArrayConstructor.h"
185226f8a9SJean Perier #include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
199ac452b2SJean Perier #include "flang/Optimizer/Builder/TemporaryStorage.h"
20ffde9f17SJean Perier #include "flang/Optimizer/Builder/Todo.h"
21ffde9f17SJean Perier #include "flang/Optimizer/HLFIR/HLFIROps.h"
22ffde9f17SJean Perier 
23ffde9f17SJean Perier // Array constructors are lowered with three different strategies.
24ffde9f17SJean Perier // All strategies are not possible with all array constructors.
25ffde9f17SJean Perier //
26ffde9f17SJean Perier // - Strategy 1: runtime approach (RuntimeTempStrategy).
27ffde9f17SJean Perier //   This strategy works will all array constructors, but will create more
28ffde9f17SJean Perier //   complex code that is harder to optimize. An allocatable temp is created,
29ffde9f17SJean Perier //   it may be unallocated if the array constructor length parameters or extent
30ffde9f17SJean Perier //   could not be computed. Then, the runtime is called to push lowered
31ffde9f17SJean Perier //   ac-value (array constructor elements) into the allocatable. The runtime
32ffde9f17SJean Perier //   will allocate or reallocate as needed while values are being pushed.
33ffde9f17SJean Perier //   In the end, the allocatable contain a temporary with all the array
34ffde9f17SJean Perier //   constructor evaluated elements.
35ffde9f17SJean Perier //
36ffde9f17SJean Perier // - Strategy 2: inlined temporary approach (InlinedTempStrategyImpl)
37ffde9f17SJean Perier //   This strategy can only be used if the array constructor extent and length
38ffde9f17SJean Perier //   parameters can be pre-computed without evaluating any ac-value, and if all
39ffde9f17SJean Perier //   of the ac-value are scalars (at least for now).
40ffde9f17SJean Perier //   A temporary is allocated inline in one go, and an index pointing at the
41ffde9f17SJean Perier //   current ac-value position in the array constructor element sequence is
42ffde9f17SJean Perier //   maintained and used to store ac-value as they are being lowered.
43ffde9f17SJean Perier //
44ffde9f17SJean Perier // - Strategy 3: "function of the indices" approach (AsElementalStrategy)
45ffde9f17SJean Perier //   This strategy can only be used if the array constructor extent and length
46ffde9f17SJean Perier //   parameters can be pre-computed and, if the array constructor is of the
47ffde9f17SJean Perier //   form "[(scalar_expr, ac-implied-do-control)]". In this case, it is lowered
48ffde9f17SJean Perier //   into an hlfir.elemental without creating any temporary in lowering. This
49ffde9f17SJean Perier //   form should maximize the chance of array temporary elision when assigning
50ffde9f17SJean Perier //   the array constructor, potentially reshaped, to an array variable.
51ffde9f17SJean Perier //
52ffde9f17SJean Perier //   The array constructor lowering looks like:
53ffde9f17SJean Perier //   ```
54ffde9f17SJean Perier //     strategy = selectArrayCtorLoweringStrategy(array-ctor-expr);
55ffde9f17SJean Perier //     for (ac-value : array-ctor-expr)
56ffde9f17SJean Perier //       if (ac-value is expression) {
57ffde9f17SJean Perier //         strategy.pushValue(ac-value);
58ffde9f17SJean Perier //       } else if (ac-value is implied-do) {
59ffde9f17SJean Perier //         strategy.startImpliedDo(lower, upper, stride);
60ebae4cc7SSlava Zakharin //         strategy.startImpliedDoScope();
61ffde9f17SJean Perier //         // lower nested values
62ebae4cc7SSlava Zakharin //         ...
63ebae4cc7SSlava Zakharin //         strategy.endImpliedDoScope();
64ffde9f17SJean Perier //       }
65ffde9f17SJean Perier //     result = strategy.finishArrayCtorLowering();
66ffde9f17SJean Perier //   ```
67ffde9f17SJean Perier 
68ffde9f17SJean Perier //===----------------------------------------------------------------------===//
69ffde9f17SJean Perier //   Definition of the lowering strategies. Each lowering strategy is defined
70ebae4cc7SSlava Zakharin //   as a class that implements "pushValue", "startImpliedDo" and
71ebae4cc7SSlava Zakharin //   "finishArrayCtorLowering". A strategy may optionally override
72ebae4cc7SSlava Zakharin //   "startImpliedDoScope" and "endImpliedDoScope" virtual methods
73ebae4cc7SSlava Zakharin //   of its base class StrategyBase.
74ffde9f17SJean Perier //===----------------------------------------------------------------------===//
75ffde9f17SJean Perier 
76ffde9f17SJean Perier namespace {
77ebae4cc7SSlava Zakharin /// Class provides common implementation of scope push/pop methods
78ebae4cc7SSlava Zakharin /// that update StatementContext scopes and SymMap bindings.
79ebae4cc7SSlava Zakharin /// They might be overridden by the lowering strategies, e.g.
80ebae4cc7SSlava Zakharin /// see AsElementalStrategy.
81ebae4cc7SSlava Zakharin class StrategyBase {
82ebae4cc7SSlava Zakharin public:
83ebae4cc7SSlava Zakharin   StrategyBase(Fortran::lower::StatementContext &stmtCtx,
84ebae4cc7SSlava Zakharin                Fortran::lower::SymMap &symMap)
85ebae4cc7SSlava Zakharin       : stmtCtx{stmtCtx}, symMap{symMap} {};
86ebae4cc7SSlava Zakharin   virtual ~StrategyBase() = default;
87ebae4cc7SSlava Zakharin 
88ebae4cc7SSlava Zakharin   virtual void startImpliedDoScope(llvm::StringRef doName,
89ebae4cc7SSlava Zakharin                                    mlir::Value indexValue) {
90ebae4cc7SSlava Zakharin     symMap.pushImpliedDoBinding(doName, indexValue);
91ebae4cc7SSlava Zakharin     stmtCtx.pushScope();
92ebae4cc7SSlava Zakharin   }
93ebae4cc7SSlava Zakharin 
94ebae4cc7SSlava Zakharin   virtual void endImpliedDoScope() {
95ebae4cc7SSlava Zakharin     stmtCtx.finalizeAndPop();
96ebae4cc7SSlava Zakharin     symMap.popImpliedDoBinding();
97ebae4cc7SSlava Zakharin   }
98ebae4cc7SSlava Zakharin 
99ebae4cc7SSlava Zakharin protected:
100ebae4cc7SSlava Zakharin   Fortran::lower::StatementContext &stmtCtx;
101ebae4cc7SSlava Zakharin   Fortran::lower::SymMap &symMap;
102ebae4cc7SSlava Zakharin };
103ebae4cc7SSlava Zakharin 
104ffde9f17SJean Perier /// Class that implements the "inlined temp strategy" to lower array
1059ac452b2SJean Perier /// constructors. It must be provided a boolean to indicate if the array
1069ac452b2SJean Perier /// constructor has any implied-do-loop.
1079ac452b2SJean Perier template <bool hasLoops>
1089ac452b2SJean Perier class InlinedTempStrategyImpl : public StrategyBase,
1099ac452b2SJean Perier                                 public fir::factory::HomogeneousScalarStack {
110ffde9f17SJean Perier   /// Name that will be given to the temporary allocation and hlfir.declare in
111ffde9f17SJean Perier   /// the IR.
112ffde9f17SJean Perier   static constexpr char tempName[] = ".tmp.arrayctor";
113ffde9f17SJean Perier 
114ffde9f17SJean Perier public:
115ffde9f17SJean Perier   /// Start lowering an array constructor according to the inline strategy.
116ffde9f17SJean Perier   /// The temporary is created right away.
117ffde9f17SJean Perier   InlinedTempStrategyImpl(mlir::Location loc, fir::FirOpBuilder &builder,
118ebae4cc7SSlava Zakharin                           Fortran::lower::StatementContext &stmtCtx,
119ebae4cc7SSlava Zakharin                           Fortran::lower::SymMap &symMap,
120ffde9f17SJean Perier                           fir::SequenceType declaredType, mlir::Value extent,
121ffde9f17SJean Perier                           llvm::ArrayRef<mlir::Value> lengths)
122ebae4cc7SSlava Zakharin       : StrategyBase{stmtCtx, symMap},
1239ac452b2SJean Perier         fir::factory::HomogeneousScalarStack{
1249ac452b2SJean Perier             loc,      builder, declaredType,
1259ac452b2SJean Perier             extent,   lengths, /*allocateOnHeap=*/true,
1269ac452b2SJean Perier             hasLoops, tempName} {}
127ffde9f17SJean Perier 
128ffde9f17SJean Perier   /// Push a lowered ac-value into the current insertion point and
129ffde9f17SJean Perier   /// increment the insertion point.
1309ac452b2SJean Perier   using fir::factory::HomogeneousScalarStack::pushValue;
131ffde9f17SJean Perier 
132ffde9f17SJean Perier   /// Start a fir.do_loop with the control from an implied-do and return
133ffde9f17SJean Perier   /// the loop induction variable that is the ac-do-variable value.
134ffde9f17SJean Perier   /// Only usable if the counter is able to track the position through loops.
135ffde9f17SJean Perier   mlir::Value startImpliedDo(mlir::Location loc, fir::FirOpBuilder &builder,
136ffde9f17SJean Perier                              mlir::Value lower, mlir::Value upper,
137ffde9f17SJean Perier                              mlir::Value stride) {
1389ac452b2SJean Perier     if constexpr (!hasLoops)
139ffde9f17SJean Perier       fir::emitFatalError(loc, "array constructor lowering is inconsistent");
140ffde9f17SJean Perier     auto loop = builder.create<fir::DoLoopOp>(loc, lower, upper, stride,
141ffde9f17SJean Perier                                               /*unordered=*/false,
142ffde9f17SJean Perier                                               /*finalCount=*/false);
143ffde9f17SJean Perier     builder.setInsertionPointToStart(loop.getBody());
144ffde9f17SJean Perier     return loop.getInductionVar();
145ffde9f17SJean Perier   }
146ffde9f17SJean Perier 
147ffde9f17SJean Perier   /// Move the temporary to an hlfir.expr value (array constructors are not
148ffde9f17SJean Perier   /// variables and cannot be further modified).
149ffde9f17SJean Perier   hlfir::Entity finishArrayCtorLowering(mlir::Location loc,
150ffde9f17SJean Perier                                         fir::FirOpBuilder &builder) {
1519ac452b2SJean Perier     return moveStackAsArrayExpr(loc, builder);
152ffde9f17SJean Perier   }
153ffde9f17SJean Perier };
154ffde9f17SJean Perier 
155ffde9f17SJean Perier /// Semantic analysis expression rewrites unroll implied do loop with
1569ac452b2SJean Perier /// compile time constant bounds (even if huge). So using a minimalistic
157ffde9f17SJean Perier /// counter greatly reduces the generated IR for simple but big array
158ffde9f17SJean Perier /// constructors [(i,i=1,constant-expr)] that are expected to be quite
159ffde9f17SJean Perier /// common.
1609ac452b2SJean Perier using LooplessInlinedTempStrategy = InlinedTempStrategyImpl</*hasLoops=*/false>;
161ffde9f17SJean Perier /// A generic memory based counter that can deal with all cases of
162ffde9f17SJean Perier /// "inlined temp strategy". The counter value is stored in a temp
163ffde9f17SJean Perier /// from which it is loaded, incremented, and stored every time an
164ffde9f17SJean Perier /// ac-value is pushed.
1659ac452b2SJean Perier using InlinedTempStrategy = InlinedTempStrategyImpl</*hasLoops=*/true>;
166ffde9f17SJean Perier 
167a9e4bb38SJean Perier /// Class that implements the "as function of the indices" lowering strategy.
168a9e4bb38SJean Perier /// It will lower [(scalar_expr(i), i=l,u,s)] to:
169a9e4bb38SJean Perier /// ```
170a9e4bb38SJean Perier ///   %extent = max((%u-%l+1)/%s, 0)
171a9e4bb38SJean Perier ///   %shape = fir.shape %extent
172a9e4bb38SJean Perier ///   %elem = hlfir.elemental %shape {
173a9e4bb38SJean Perier ///     ^bb0(%pos:index):
174a9e4bb38SJean Perier ///      %i = %l+(%i-1)*%s
175a9e4bb38SJean Perier ///      %value = scalar_expr(%i)
176a9e4bb38SJean Perier ///       hlfir.yield_element %value
177a9e4bb38SJean Perier ///    }
178a9e4bb38SJean Perier /// ```
179a9e4bb38SJean Perier /// That way, no temporary is created in lowering, and if the array constructor
180a9e4bb38SJean Perier /// is part of a more complex elemental expression, or an assignment, it will be
181a9e4bb38SJean Perier /// trivial to "inline" it in the expression or assignment loops if allowed by
182a9e4bb38SJean Perier /// alias analysis.
183a9e4bb38SJean Perier /// This lowering is however only possible for the form of array constructors as
184a9e4bb38SJean Perier /// in the illustration above. It could be extended to deeper independent
185a9e4bb38SJean Perier /// implied-do nest and wrapped in an hlfir.reshape to a rank 1 array. But this
186a9e4bb38SJean Perier /// op does not exist yet, so this is left for the future if it appears
187a9e4bb38SJean Perier /// profitable.
188ebae4cc7SSlava Zakharin class AsElementalStrategy : public StrategyBase {
189a9e4bb38SJean Perier public:
190a9e4bb38SJean Perier   /// The constructor only gathers the operands to create the hlfir.elemental.
191a9e4bb38SJean Perier   AsElementalStrategy(mlir::Location loc, fir::FirOpBuilder &builder,
192ebae4cc7SSlava Zakharin                       Fortran::lower::StatementContext &stmtCtx,
193ebae4cc7SSlava Zakharin                       Fortran::lower::SymMap &symMap,
194a9e4bb38SJean Perier                       fir::SequenceType declaredType, mlir::Value extent,
195a9e4bb38SJean Perier                       llvm::ArrayRef<mlir::Value> lengths)
196ebae4cc7SSlava Zakharin       : StrategyBase{stmtCtx, symMap}, shape{builder.genShape(loc, {extent})},
197*6f8ef5adSKazu Hirata         lengthParams{lengths}, exprType{getExprType(declaredType)} {}
198a9e4bb38SJean Perier 
199a9e4bb38SJean Perier   static hlfir::ExprType getExprType(fir::SequenceType declaredType) {
200a9e4bb38SJean Perier     // Note: 7.8 point 4: the dynamic type of an array constructor is its static
201a9e4bb38SJean Perier     // type, it is not polymorphic.
202a9e4bb38SJean Perier     return hlfir::ExprType::get(declaredType.getContext(),
203a9e4bb38SJean Perier                                 declaredType.getShape(),
204a9e4bb38SJean Perier                                 declaredType.getEleTy(),
205a9e4bb38SJean Perier                                 /*isPolymorphic=*/false);
206a9e4bb38SJean Perier   }
207a9e4bb38SJean Perier 
208a9e4bb38SJean Perier   /// Create the hlfir.elemental and compute the ac-implied-do-index value
209a9e4bb38SJean Perier   /// given the lower bound and stride (compute "%i" in the illustration above).
210a9e4bb38SJean Perier   mlir::Value startImpliedDo(mlir::Location loc, fir::FirOpBuilder &builder,
211a9e4bb38SJean Perier                              mlir::Value lower, mlir::Value upper,
212a9e4bb38SJean Perier                              mlir::Value stride) {
213a9e4bb38SJean Perier     assert(!elementalOp && "expected only one implied-do");
214a9e4bb38SJean Perier     mlir::Value one =
215a9e4bb38SJean Perier         builder.createIntegerConstant(loc, builder.getIndexType(), 1);
21693fea7ddSSlava Zakharin     elementalOp = builder.create<hlfir::ElementalOp>(
21793fea7ddSSlava Zakharin         loc, exprType, shape,
21893fea7ddSSlava Zakharin         /*mold=*/nullptr, lengthParams, /*isUnordered=*/true);
219a9e4bb38SJean Perier     builder.setInsertionPointToStart(elementalOp.getBody());
220a9e4bb38SJean Perier     // implied-do-index = lower+((i-1)*stride)
221a9e4bb38SJean Perier     mlir::Value diff = builder.create<mlir::arith::SubIOp>(
222a9e4bb38SJean Perier         loc, elementalOp.getIndices()[0], one);
223a9e4bb38SJean Perier     mlir::Value mul = builder.create<mlir::arith::MulIOp>(loc, diff, stride);
224a9e4bb38SJean Perier     mlir::Value add = builder.create<mlir::arith::AddIOp>(loc, lower, mul);
225a9e4bb38SJean Perier     return add;
226a9e4bb38SJean Perier   }
227a9e4bb38SJean Perier 
228a9e4bb38SJean Perier   /// Create the elemental hlfir.yield_element with the scalar ac-value.
229a9e4bb38SJean Perier   void pushValue(mlir::Location loc, fir::FirOpBuilder &builder,
230a9e4bb38SJean Perier                  hlfir::Entity value) {
231a9e4bb38SJean Perier     assert(value.isScalar() && "cannot use hlfir.elemental with array values");
232a9e4bb38SJean Perier     assert(elementalOp && "array constructor must contain an outer implied-do");
233a9e4bb38SJean Perier     mlir::Value elementResult = value;
234a9e4bb38SJean Perier     if (fir::isa_trivial(elementResult.getType()))
235a9e4bb38SJean Perier       elementResult =
236a9e4bb38SJean Perier           builder.createConvert(loc, exprType.getElementType(), elementResult);
237ebae4cc7SSlava Zakharin 
238ebae4cc7SSlava Zakharin     // The clean-ups associated with the implied-do body operations
239ebae4cc7SSlava Zakharin     // must be initiated before the YieldElementOp, so we have to pop the scope
240ebae4cc7SSlava Zakharin     // right now.
241ebae4cc7SSlava Zakharin     stmtCtx.finalizeAndPop();
242ebae4cc7SSlava Zakharin 
243b9e435cbSSlava Zakharin     // This is a hacky way to get rid of the DestroyOp clean-up
244b9e435cbSSlava Zakharin     // associated with the final ac-value result if it is hlfir.expr.
245b9e435cbSSlava Zakharin     // Example:
246b9e435cbSSlava Zakharin     //   ... = (/(REPEAT(REPEAT(CHAR(i),2),2),i=1,n)/)
247b9e435cbSSlava Zakharin     // Each intrinsic call lowering will produce hlfir.expr result
248b9e435cbSSlava Zakharin     // with the associated clean-up, but only the last of them
249b9e435cbSSlava Zakharin     // is wrong. It is wrong because the value is used in hlfir.yield_element,
250b9e435cbSSlava Zakharin     // so it cannot be destroyed.
251b9e435cbSSlava Zakharin     mlir::Operation *destroyOp = nullptr;
252b9e435cbSSlava Zakharin     for (mlir::Operation *useOp : elementResult.getUsers())
253b9e435cbSSlava Zakharin       if (mlir::isa<hlfir::DestroyOp>(useOp)) {
254b9e435cbSSlava Zakharin         if (destroyOp)
255b9e435cbSSlava Zakharin           fir::emitFatalError(loc,
256b9e435cbSSlava Zakharin                               "multiple DestroyOp's for ac-value expression");
257b9e435cbSSlava Zakharin         destroyOp = useOp;
258b9e435cbSSlava Zakharin       }
259b9e435cbSSlava Zakharin 
260b9e435cbSSlava Zakharin     if (destroyOp)
261b9e435cbSSlava Zakharin       destroyOp->erase();
262b9e435cbSSlava Zakharin 
263a9e4bb38SJean Perier     builder.create<hlfir::YieldElementOp>(loc, elementResult);
264a9e4bb38SJean Perier   }
265a9e4bb38SJean Perier 
266ebae4cc7SSlava Zakharin   // Override the default, because the context scope must be popped in
267ebae4cc7SSlava Zakharin   // pushValue().
268ebae4cc7SSlava Zakharin   virtual void endImpliedDoScope() override { symMap.popImpliedDoBinding(); }
269ebae4cc7SSlava Zakharin 
270a9e4bb38SJean Perier   /// Return the created hlfir.elemental.
271a9e4bb38SJean Perier   hlfir::Entity finishArrayCtorLowering(mlir::Location loc,
272a9e4bb38SJean Perier                                         fir::FirOpBuilder &builder) {
273a9e4bb38SJean Perier     return hlfir::Entity{elementalOp};
274a9e4bb38SJean Perier   }
275a9e4bb38SJean Perier 
276a9e4bb38SJean Perier private:
277a9e4bb38SJean Perier   mlir::Value shape;
278a9e4bb38SJean Perier   llvm::SmallVector<mlir::Value> lengthParams;
279a9e4bb38SJean Perier   hlfir::ExprType exprType;
280a9e4bb38SJean Perier   hlfir::ElementalOp elementalOp{};
281a9e4bb38SJean Perier };
282ffde9f17SJean Perier 
2839683a9c9SJean Perier /// Class that implements the "runtime temp strategy" to lower array
2849683a9c9SJean Perier /// constructors.
285ebae4cc7SSlava Zakharin class RuntimeTempStrategy : public StrategyBase {
2869683a9c9SJean Perier   /// Name that will be given to the temporary allocation and hlfir.declare in
2879683a9c9SJean Perier   /// the IR.
2889683a9c9SJean Perier   static constexpr char tempName[] = ".tmp.arrayctor";
2899683a9c9SJean Perier 
2909683a9c9SJean Perier public:
2919683a9c9SJean Perier   /// Start lowering an array constructor according to the runtime strategy.
2929683a9c9SJean Perier   /// The temporary is only created if the extents and length parameters are
2939683a9c9SJean Perier   /// already known. Otherwise, the handling of the allocation (and
2949683a9c9SJean Perier   /// reallocation) is left up to the runtime.
2959683a9c9SJean Perier   /// \p extent is the pre-computed extent of the array constructor, if it could
2969683a9c9SJean Perier   /// be pre-computed. It is std::nullopt otherwise.
2979683a9c9SJean Perier   /// \p lengths are the pre-computed length parameters of the array
2989683a9c9SJean Perier   /// constructor, if they could be precomputed. \p missingLengthParameters is
2999683a9c9SJean Perier   /// set to true if the length parameters could not be precomputed.
3009683a9c9SJean Perier   RuntimeTempStrategy(mlir::Location loc, fir::FirOpBuilder &builder,
301ebae4cc7SSlava Zakharin                       Fortran::lower::StatementContext &stmtCtx,
302ebae4cc7SSlava Zakharin                       Fortran::lower::SymMap &symMap,
3039683a9c9SJean Perier                       fir::SequenceType declaredType,
3049683a9c9SJean Perier                       std::optional<mlir::Value> extent,
3059683a9c9SJean Perier                       llvm::ArrayRef<mlir::Value> lengths,
3069683a9c9SJean Perier                       bool missingLengthParameters)
307ebae4cc7SSlava Zakharin       : StrategyBase{stmtCtx, symMap},
308ebae4cc7SSlava Zakharin         arrayConstructorElementType{declaredType.getEleTy()} {
3099683a9c9SJean Perier     mlir::Type heapType = fir::HeapType::get(declaredType);
3109683a9c9SJean Perier     mlir::Type boxType = fir::BoxType::get(heapType);
3119683a9c9SJean Perier     allocatableTemp = builder.createTemporary(loc, boxType, tempName);
3129683a9c9SJean Perier     mlir::Value initialBoxValue;
3139683a9c9SJean Perier     if (extent && !missingLengthParameters) {
3149683a9c9SJean Perier       llvm::SmallVector<mlir::Value, 1> extents{*extent};
3159683a9c9SJean Perier       mlir::Value tempStorage = builder.createHeapTemporary(
3169683a9c9SJean Perier           loc, declaredType, tempName, extents, lengths);
3179683a9c9SJean Perier       mlir::Value shape = builder.genShape(loc, extents);
3189683a9c9SJean Perier       declare = builder.create<hlfir::DeclareOp>(
3199683a9c9SJean Perier           loc, tempStorage, tempName, shape, lengths,
3201710c8cfSSlava Zakharin           /*dummy_scope=*/nullptr, fir::FortranVariableFlagsAttr{});
3219683a9c9SJean Perier       initialBoxValue =
3229683a9c9SJean Perier           builder.createBox(loc, boxType, declare->getOriginalBase(), shape,
3239683a9c9SJean Perier                             /*slice=*/mlir::Value{}, lengths, /*tdesc=*/{});
3249683a9c9SJean Perier     } else {
3259683a9c9SJean Perier       // The runtime will have to do the initial allocation.
3269683a9c9SJean Perier       // The declare operation cannot be emitted in this case since the final
3279683a9c9SJean Perier       // array constructor has not yet been allocated. Instead, the resulting
3289683a9c9SJean Perier       // temporary variable will be extracted from the allocatable descriptor
3299683a9c9SJean Perier       // after all the API calls.
3309683a9c9SJean Perier       // Prepare the initial state of the allocatable descriptor with a
3319683a9c9SJean Perier       // deallocated status and all the available knowledge about the extent
3329683a9c9SJean Perier       // and length parameters.
333*6f8ef5adSKazu Hirata       llvm::SmallVector<mlir::Value> emboxLengths(lengths);
3349683a9c9SJean Perier       if (!extent)
3359683a9c9SJean Perier         extent = builder.createIntegerConstant(loc, builder.getIndexType(), 0);
3369683a9c9SJean Perier       if (missingLengthParameters) {
337fac349a1SChristian Sigg         if (mlir::isa<fir::CharacterType>(declaredType.getEleTy()))
3389683a9c9SJean Perier           emboxLengths.push_back(builder.createIntegerConstant(
3399683a9c9SJean Perier               loc, builder.getCharacterLengthType(), 0));
3409683a9c9SJean Perier         else
3419683a9c9SJean Perier           TODO(loc,
3429683a9c9SJean Perier                "parametrized derived type array constructor without type-spec");
3439683a9c9SJean Perier       }
3449683a9c9SJean Perier       mlir::Value nullAddr = builder.createNullConstant(loc, heapType);
3459683a9c9SJean Perier       mlir::Value shape = builder.genShape(loc, {*extent});
3469683a9c9SJean Perier       initialBoxValue = builder.createBox(loc, boxType, nullAddr, shape,
3479683a9c9SJean Perier                                           /*slice=*/mlir::Value{}, emboxLengths,
3489683a9c9SJean Perier                                           /*tdesc=*/{});
3499683a9c9SJean Perier     }
3509683a9c9SJean Perier     builder.create<fir::StoreOp>(loc, initialBoxValue, allocatableTemp);
3519683a9c9SJean Perier     arrayConstructorVector = fir::runtime::genInitArrayConstructorVector(
3529683a9c9SJean Perier         loc, builder, allocatableTemp,
3539683a9c9SJean Perier         builder.createBool(loc, missingLengthParameters));
3549683a9c9SJean Perier   }
3559683a9c9SJean Perier 
3569683a9c9SJean Perier   bool useSimplePushRuntime(hlfir::Entity value) {
3579683a9c9SJean Perier     return value.isScalar() &&
358fac349a1SChristian Sigg            !mlir::isa<fir::CharacterType>(arrayConstructorElementType) &&
3599683a9c9SJean Perier            !fir::isRecordWithAllocatableMember(arrayConstructorElementType) &&
3609683a9c9SJean Perier            !fir::isRecordWithTypeParameters(arrayConstructorElementType);
3619683a9c9SJean Perier   }
3629683a9c9SJean Perier 
3639683a9c9SJean Perier   /// Push a lowered ac-value into the array constructor vector using
3649683a9c9SJean Perier   /// the runtime API.
3659683a9c9SJean Perier   void pushValue(mlir::Location loc, fir::FirOpBuilder &builder,
3669683a9c9SJean Perier                  hlfir::Entity value) {
3679683a9c9SJean Perier     if (useSimplePushRuntime(value)) {
3689683a9c9SJean Perier       auto [addrExv, cleanUp] = hlfir::convertToAddress(
3699683a9c9SJean Perier           loc, builder, value, arrayConstructorElementType);
3709683a9c9SJean Perier       mlir::Value addr = fir::getBase(addrExv);
371fac349a1SChristian Sigg       if (mlir::isa<fir::BaseBoxType>(addr.getType()))
3729683a9c9SJean Perier         addr = builder.create<fir::BoxAddrOp>(loc, addr);
3739683a9c9SJean Perier       fir::runtime::genPushArrayConstructorSimpleScalar(
3749683a9c9SJean Perier           loc, builder, arrayConstructorVector, addr);
3759683a9c9SJean Perier       if (cleanUp)
3769683a9c9SJean Perier         (*cleanUp)();
3779683a9c9SJean Perier       return;
3789683a9c9SJean Perier     }
3799683a9c9SJean Perier     auto [boxExv, cleanUp] =
3809683a9c9SJean Perier         hlfir::convertToBox(loc, builder, value, arrayConstructorElementType);
3819683a9c9SJean Perier     fir::runtime::genPushArrayConstructorValue(
3829683a9c9SJean Perier         loc, builder, arrayConstructorVector, fir::getBase(boxExv));
3839683a9c9SJean Perier     if (cleanUp)
3849683a9c9SJean Perier       (*cleanUp)();
3859683a9c9SJean Perier   }
3869683a9c9SJean Perier 
3879683a9c9SJean Perier   /// Start a fir.do_loop with the control from an implied-do and return
3889683a9c9SJean Perier   /// the loop induction variable that is the ac-do-variable value.
3899683a9c9SJean Perier   mlir::Value startImpliedDo(mlir::Location loc, fir::FirOpBuilder &builder,
3909683a9c9SJean Perier                              mlir::Value lower, mlir::Value upper,
3919683a9c9SJean Perier                              mlir::Value stride) {
3929683a9c9SJean Perier     auto loop = builder.create<fir::DoLoopOp>(loc, lower, upper, stride,
3939683a9c9SJean Perier                                               /*unordered=*/false,
3949683a9c9SJean Perier                                               /*finalCount=*/false);
3959683a9c9SJean Perier     builder.setInsertionPointToStart(loop.getBody());
3969683a9c9SJean Perier     return loop.getInductionVar();
3979683a9c9SJean Perier   }
3989683a9c9SJean Perier 
3999683a9c9SJean Perier   /// Move the temporary to an hlfir.expr value (array constructors are not
4009683a9c9SJean Perier   /// variables and cannot be further modified).
4019683a9c9SJean Perier   hlfir::Entity finishArrayCtorLowering(mlir::Location loc,
4029683a9c9SJean Perier                                         fir::FirOpBuilder &builder) {
4039683a9c9SJean Perier     // Temp is created using createHeapTemporary, or allocated on the heap
4049683a9c9SJean Perier     // by the runtime.
4059683a9c9SJean Perier     mlir::Value mustFree = builder.createBool(loc, true);
4069683a9c9SJean Perier     mlir::Value temp;
4079683a9c9SJean Perier     if (declare)
4089683a9c9SJean Perier       temp = declare->getBase();
4099683a9c9SJean Perier     else
4109683a9c9SJean Perier       temp = hlfir::derefPointersAndAllocatables(
4119683a9c9SJean Perier           loc, builder, hlfir::Entity{allocatableTemp});
4129683a9c9SJean Perier     auto hlfirExpr = builder.create<hlfir::AsExprOp>(loc, temp, mustFree);
4139683a9c9SJean Perier     return hlfir::Entity{hlfirExpr};
4149683a9c9SJean Perier   }
4159683a9c9SJean Perier 
4169683a9c9SJean Perier private:
4179683a9c9SJean Perier   /// Element type of the array constructor being built.
4189683a9c9SJean Perier   mlir::Type arrayConstructorElementType;
4199683a9c9SJean Perier   /// Allocatable descriptor for the storage of the array constructor being
4209683a9c9SJean Perier   /// built.
4219683a9c9SJean Perier   mlir::Value allocatableTemp;
4229683a9c9SJean Perier   /// Structure that allows the runtime API to maintain the status of
4239683a9c9SJean Perier   /// of the array constructor being built between two API calls.
4249683a9c9SJean Perier   mlir::Value arrayConstructorVector;
4259683a9c9SJean Perier   /// DeclareOp for the array constructor storage, if it was possible to
4269683a9c9SJean Perier   /// allocate it before any API calls.
4279683a9c9SJean Perier   std::optional<hlfir::DeclareOp> declare;
4289683a9c9SJean Perier };
429ffde9f17SJean Perier 
430ffde9f17SJean Perier /// Wrapper class that dispatch to the selected array constructor lowering
431ffde9f17SJean Perier /// strategy and does nothing else.
432ffde9f17SJean Perier class ArrayCtorLoweringStrategy {
433ffde9f17SJean Perier public:
434ffde9f17SJean Perier   template <typename A>
435ffde9f17SJean Perier   ArrayCtorLoweringStrategy(A &&impl) : implVariant{std::forward<A>(impl)} {}
436ffde9f17SJean Perier 
437ffde9f17SJean Perier   void pushValue(mlir::Location loc, fir::FirOpBuilder &builder,
438ffde9f17SJean Perier                  hlfir::Entity value) {
43977d8cfb3SAlexander Shaposhnikov     return Fortran::common::visit(
440ffde9f17SJean Perier         [&](auto &impl) { return impl.pushValue(loc, builder, value); },
441ffde9f17SJean Perier         implVariant);
442ffde9f17SJean Perier   }
443ffde9f17SJean Perier 
444ffde9f17SJean Perier   mlir::Value startImpliedDo(mlir::Location loc, fir::FirOpBuilder &builder,
445ffde9f17SJean Perier                              mlir::Value lower, mlir::Value upper,
446ffde9f17SJean Perier                              mlir::Value stride) {
44777d8cfb3SAlexander Shaposhnikov     return Fortran::common::visit(
448ffde9f17SJean Perier         [&](auto &impl) {
449ffde9f17SJean Perier           return impl.startImpliedDo(loc, builder, lower, upper, stride);
450ffde9f17SJean Perier         },
451ffde9f17SJean Perier         implVariant);
452ffde9f17SJean Perier   }
453ffde9f17SJean Perier 
454ffde9f17SJean Perier   hlfir::Entity finishArrayCtorLowering(mlir::Location loc,
455ffde9f17SJean Perier                                         fir::FirOpBuilder &builder) {
45677d8cfb3SAlexander Shaposhnikov     return Fortran::common::visit(
457ffde9f17SJean Perier         [&](auto &impl) { return impl.finishArrayCtorLowering(loc, builder); },
458ffde9f17SJean Perier         implVariant);
459ffde9f17SJean Perier   }
460ffde9f17SJean Perier 
461ebae4cc7SSlava Zakharin   void startImpliedDoScope(llvm::StringRef doName, mlir::Value indexValue) {
46277d8cfb3SAlexander Shaposhnikov     Fortran::common::visit(
463ebae4cc7SSlava Zakharin         [&](auto &impl) {
464ebae4cc7SSlava Zakharin           return impl.startImpliedDoScope(doName, indexValue);
465ebae4cc7SSlava Zakharin         },
466ebae4cc7SSlava Zakharin         implVariant);
467ebae4cc7SSlava Zakharin   }
468ebae4cc7SSlava Zakharin 
469ebae4cc7SSlava Zakharin   void endImpliedDoScope() {
47077d8cfb3SAlexander Shaposhnikov     Fortran::common::visit([&](auto &impl) { return impl.endImpliedDoScope(); },
471ebae4cc7SSlava Zakharin                            implVariant);
472ebae4cc7SSlava Zakharin   }
473ebae4cc7SSlava Zakharin 
474ffde9f17SJean Perier private:
475a9e4bb38SJean Perier   std::variant<InlinedTempStrategy, LooplessInlinedTempStrategy,
4769683a9c9SJean Perier                AsElementalStrategy, RuntimeTempStrategy>
477a9e4bb38SJean Perier       implVariant;
478ffde9f17SJean Perier };
479ffde9f17SJean Perier } // namespace
480ffde9f17SJean Perier 
481ffde9f17SJean Perier //===----------------------------------------------------------------------===//
482ffde9f17SJean Perier //   Definition of selectArrayCtorLoweringStrategy and its helpers.
483ffde9f17SJean Perier //   This is the code that analyses the evaluate::ArrayConstructor<T>,
484ffde9f17SJean Perier //   pre-lowers the array constructor extent and length parameters if it can,
485ffde9f17SJean Perier //   and chooses the lowering strategy.
486ffde9f17SJean Perier //===----------------------------------------------------------------------===//
487ffde9f17SJean Perier 
488bc991d94SJean Perier /// Helper to lower a scalar extent expression (like implied-do bounds).
489bc991d94SJean Perier static mlir::Value lowerExtentExpr(mlir::Location loc,
490bc991d94SJean Perier                                    Fortran::lower::AbstractConverter &converter,
491bc991d94SJean Perier                                    Fortran::lower::SymMap &symMap,
492bc991d94SJean Perier                                    Fortran::lower::StatementContext &stmtCtx,
493bc991d94SJean Perier                                    const Fortran::evaluate::ExtentExpr &expr) {
494bc991d94SJean Perier   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
495bc991d94SJean Perier   mlir::IndexType idxTy = builder.getIndexType();
496bc991d94SJean Perier   hlfir::Entity value = Fortran::lower::convertExprToHLFIR(
497bc991d94SJean Perier       loc, converter, toEvExpr(expr), symMap, stmtCtx);
498bc991d94SJean Perier   value = hlfir::loadTrivialScalar(loc, builder, value);
499bc991d94SJean Perier   return builder.createConvert(loc, idxTy, value);
500bc991d94SJean Perier }
501bc991d94SJean Perier 
502ffde9f17SJean Perier namespace {
503ffde9f17SJean Perier /// Helper class to lower the array constructor type and its length parameters.
504ffde9f17SJean Perier /// The length parameters, if any, are only lowered if this does not require
505ffde9f17SJean Perier /// evaluating an ac-value.
506ffde9f17SJean Perier template <typename T>
507ffde9f17SJean Perier struct LengthAndTypeCollector {
508ffde9f17SJean Perier   static mlir::Type collect(mlir::Location,
509ffde9f17SJean Perier                             Fortran::lower::AbstractConverter &converter,
510ffde9f17SJean Perier                             const Fortran::evaluate::ArrayConstructor<T> &,
511ffde9f17SJean Perier                             Fortran::lower::SymMap &,
512ffde9f17SJean Perier                             Fortran::lower::StatementContext &,
513ffde9f17SJean Perier                             mlir::SmallVectorImpl<mlir::Value> &) {
514ffde9f17SJean Perier     // Numerical and Logical types.
515ffde9f17SJean Perier     return Fortran::lower::getFIRType(&converter.getMLIRContext(), T::category,
516ffde9f17SJean Perier                                       T::kind, /*lenParams*/ {});
517ffde9f17SJean Perier   }
518ffde9f17SJean Perier };
519ffde9f17SJean Perier 
520ffde9f17SJean Perier template <>
521ffde9f17SJean Perier struct LengthAndTypeCollector<Fortran::evaluate::SomeDerived> {
522ffde9f17SJean Perier   static mlir::Type collect(
523ffde9f17SJean Perier       mlir::Location loc, Fortran::lower::AbstractConverter &converter,
524ffde9f17SJean Perier       const Fortran::evaluate::ArrayConstructor<Fortran::evaluate::SomeDerived>
525ffde9f17SJean Perier           &arrayCtorExpr,
526ffde9f17SJean Perier       Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
527ffde9f17SJean Perier       mlir::SmallVectorImpl<mlir::Value> &lengths) {
528bc991d94SJean Perier     // Array constructors cannot be unlimited polymorphic (C7113), so there must
529bc991d94SJean Perier     // be a derived type spec available.
530bc991d94SJean Perier     return Fortran::lower::translateDerivedTypeToFIRType(
531bc991d94SJean Perier         converter, arrayCtorExpr.result().derivedTypeSpec());
532ffde9f17SJean Perier   }
533ffde9f17SJean Perier };
534ffde9f17SJean Perier 
535ffde9f17SJean Perier template <int Kind>
536ffde9f17SJean Perier using Character =
537ffde9f17SJean Perier     Fortran::evaluate::Type<Fortran::common::TypeCategory::Character, Kind>;
538ffde9f17SJean Perier template <int Kind>
539ffde9f17SJean Perier struct LengthAndTypeCollector<Character<Kind>> {
540ffde9f17SJean Perier   static mlir::Type collect(
541ffde9f17SJean Perier       mlir::Location loc, Fortran::lower::AbstractConverter &converter,
542ffde9f17SJean Perier       const Fortran::evaluate::ArrayConstructor<Character<Kind>> &arrayCtorExpr,
543ffde9f17SJean Perier       Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
544ffde9f17SJean Perier       mlir::SmallVectorImpl<mlir::Value> &lengths) {
545bc991d94SJean Perier     llvm::SmallVector<Fortran::lower::LenParameterTy> typeLengths;
546bc991d94SJean Perier     if (const Fortran::evaluate::ExtentExpr *lenExpr = arrayCtorExpr.LEN()) {
547bc991d94SJean Perier       lengths.push_back(
548bc991d94SJean Perier           lowerExtentExpr(loc, converter, symMap, stmtCtx, *lenExpr));
549bc991d94SJean Perier       if (std::optional<std::int64_t> cstLen =
550bc991d94SJean Perier               Fortran::evaluate::ToInt64(*lenExpr))
551bc991d94SJean Perier         typeLengths.push_back(*cstLen);
552bc991d94SJean Perier     }
553bc991d94SJean Perier     return Fortran::lower::getFIRType(&converter.getMLIRContext(),
554bc991d94SJean Perier                                       Fortran::common::TypeCategory::Character,
555bc991d94SJean Perier                                       Kind, typeLengths);
556ffde9f17SJean Perier   }
557ffde9f17SJean Perier };
558ffde9f17SJean Perier } // namespace
559ffde9f17SJean Perier 
560ffde9f17SJean Perier /// Does the array constructor have length parameters that
561ffde9f17SJean Perier /// LengthAndTypeCollector::collect could not lower because this requires
562ffde9f17SJean Perier /// lowering an ac-value and must be delayed?
5639683a9c9SJean Perier static bool missingLengthParameters(mlir::Type elementType,
564ffde9f17SJean Perier                                     llvm::ArrayRef<mlir::Value> lengths) {
565fac349a1SChristian Sigg   return (mlir::isa<fir::CharacterType>(elementType) ||
566ffde9f17SJean Perier           fir::isRecordWithTypeParameters(elementType)) &&
567ffde9f17SJean Perier          lengths.empty();
568ffde9f17SJean Perier }
569ffde9f17SJean Perier 
570ffde9f17SJean Perier namespace {
571ffde9f17SJean Perier /// Structure that analyses the ac-value and implied-do of
572ffde9f17SJean Perier /// evaluate::ArrayConstructor before they are lowered. It does not generate any
573ffde9f17SJean Perier /// IR. The result of this analysis pass is used to select the lowering
574ffde9f17SJean Perier /// strategy.
575ffde9f17SJean Perier struct ArrayCtorAnalysis {
576ffde9f17SJean Perier   template <typename T>
577ffde9f17SJean Perier   ArrayCtorAnalysis(
578a9e4bb38SJean Perier       Fortran::evaluate::FoldingContext &,
579ffde9f17SJean Perier       const Fortran::evaluate::ArrayConstructor<T> &arrayCtorExpr);
580ffde9f17SJean Perier 
581ffde9f17SJean Perier   // Can the array constructor easily be rewritten into an hlfir.elemental ?
582a9e4bb38SJean Perier   bool isSingleImpliedDoWithOneScalarPureExpr() const {
583ffde9f17SJean Perier     return !anyArrayExpr && isPerfectLoopNest &&
584a9e4bb38SJean Perier            innerNumberOfExprIfPrefectNest == 1 && depthIfPerfectLoopNest == 1 &&
585a9e4bb38SJean Perier            innerExprIsPureIfPerfectNest;
586ffde9f17SJean Perier   }
587ffde9f17SJean Perier 
588a9e4bb38SJean Perier   bool anyImpliedDo = false;
589a9e4bb38SJean Perier   bool anyArrayExpr = false;
590a9e4bb38SJean Perier   bool isPerfectLoopNest = true;
591a9e4bb38SJean Perier   bool innerExprIsPureIfPerfectNest = false;
592ffde9f17SJean Perier   std::int64_t innerNumberOfExprIfPrefectNest = 0;
593ffde9f17SJean Perier   std::int64_t depthIfPerfectLoopNest = 0;
594ffde9f17SJean Perier };
595ffde9f17SJean Perier } // namespace
596ffde9f17SJean Perier 
597ffde9f17SJean Perier template <typename T>
598ffde9f17SJean Perier ArrayCtorAnalysis::ArrayCtorAnalysis(
599a9e4bb38SJean Perier     Fortran::evaluate::FoldingContext &foldingContext,
600ffde9f17SJean Perier     const Fortran::evaluate::ArrayConstructor<T> &arrayCtorExpr) {
601ffde9f17SJean Perier   llvm::SmallVector<const Fortran::evaluate::ArrayConstructorValues<T> *>
602ffde9f17SJean Perier       arrayValueListStack{&arrayCtorExpr};
603ffde9f17SJean Perier   // Loop through the ac-value-list(s) of the array constructor.
604ffde9f17SJean Perier   while (!arrayValueListStack.empty()) {
605ffde9f17SJean Perier     std::int64_t localNumberOfImpliedDo = 0;
606ffde9f17SJean Perier     std::int64_t localNumberOfExpr = 0;
607ffde9f17SJean Perier     // Loop though the ac-value of an ac-value list, and add any nested
608ffde9f17SJean Perier     // ac-value-list of ac-implied-do to the stack.
609a9e4bb38SJean Perier     const Fortran::evaluate::ArrayConstructorValues<T> *currentArrayValueList =
610a9e4bb38SJean Perier         arrayValueListStack.pop_back_val();
611ffde9f17SJean Perier     for (const Fortran::evaluate::ArrayConstructorValue<T> &acValue :
612a9e4bb38SJean Perier          *currentArrayValueList)
61377d8cfb3SAlexander Shaposhnikov       Fortran::common::visit(
61477d8cfb3SAlexander Shaposhnikov           Fortran::common::visitors{
615ffde9f17SJean Perier               [&](const Fortran::evaluate::ImpliedDo<T> &impledDo) {
616ffde9f17SJean Perier                 arrayValueListStack.push_back(&impledDo.values());
617ffde9f17SJean Perier                 localNumberOfImpliedDo++;
618ffde9f17SJean Perier               },
619ffde9f17SJean Perier               [&](const Fortran::evaluate::Expr<T> &expr) {
620ffde9f17SJean Perier                 localNumberOfExpr++;
621ffde9f17SJean Perier                 anyArrayExpr = anyArrayExpr || expr.Rank() > 0;
622ffde9f17SJean Perier               }},
623ffde9f17SJean Perier           acValue.u);
624ffde9f17SJean Perier     anyImpliedDo = anyImpliedDo || localNumberOfImpliedDo > 0;
625ffde9f17SJean Perier 
626ffde9f17SJean Perier     if (localNumberOfImpliedDo == 0) {
627ffde9f17SJean Perier       // Leaf ac-value-list in the array constructor ac-value tree.
628a9e4bb38SJean Perier       if (isPerfectLoopNest) {
629ffde9f17SJean Perier         // This this the only leaf of the array-constructor (the array
630ffde9f17SJean Perier         // constructor is a nest of single implied-do with a list of expression
631ffde9f17SJean Perier         // in the last deeper implied do). e.g: "[((i+j, i=1,n)j=1,m)]".
632ffde9f17SJean Perier         innerNumberOfExprIfPrefectNest = localNumberOfExpr;
633a9e4bb38SJean Perier         if (localNumberOfExpr == 1)
634a9e4bb38SJean Perier           innerExprIsPureIfPerfectNest = !Fortran::evaluate::FindImpureCall(
635a9e4bb38SJean Perier               foldingContext, toEvExpr(std::get<Fortran::evaluate::Expr<T>>(
636a9e4bb38SJean Perier                                   currentArrayValueList->begin()->u)));
637a9e4bb38SJean Perier       }
638ffde9f17SJean Perier     } else if (localNumberOfImpliedDo == 1 && localNumberOfExpr == 0) {
639ffde9f17SJean Perier       // Perfect implied-do nest new level.
640ffde9f17SJean Perier       ++depthIfPerfectLoopNest;
641ffde9f17SJean Perier     } else {
642ffde9f17SJean Perier       // More than one implied-do, or at least one implied-do and an expr
643ffde9f17SJean Perier       // at that level. This will not form a perfect nest. Examples:
644ffde9f17SJean Perier       // "[a, (i, i=1,n)]" or "[(i, i=1,n), (j, j=1,m)]".
645ffde9f17SJean Perier       isPerfectLoopNest = false;
646ffde9f17SJean Perier     }
647ffde9f17SJean Perier   }
648ffde9f17SJean Perier }
649ffde9f17SJean Perier 
650ffde9f17SJean Perier /// Does \p expr contain no calls to user function?
651ffde9f17SJean Perier static bool isCallFreeExpr(const Fortran::evaluate::ExtentExpr &expr) {
652ffde9f17SJean Perier   for (const Fortran::semantics::Symbol &symbol :
653ffde9f17SJean Perier        Fortran::evaluate::CollectSymbols(expr))
654ffde9f17SJean Perier     if (Fortran::semantics::IsProcedure(symbol))
655ffde9f17SJean Perier       return false;
656ffde9f17SJean Perier   return true;
657ffde9f17SJean Perier }
658ffde9f17SJean Perier 
659ffde9f17SJean Perier /// Core function that pre-lowers the extent and length parameters of
660ffde9f17SJean Perier /// array constructors if it can, runs the ac-value analysis and
661ffde9f17SJean Perier /// select the lowering strategy accordingly.
662ffde9f17SJean Perier template <typename T>
663ffde9f17SJean Perier static ArrayCtorLoweringStrategy selectArrayCtorLoweringStrategy(
664ffde9f17SJean Perier     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
665ffde9f17SJean Perier     const Fortran::evaluate::ArrayConstructor<T> &arrayCtorExpr,
666ffde9f17SJean Perier     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
667ffde9f17SJean Perier   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
668ffde9f17SJean Perier   mlir::Type idxType = builder.getIndexType();
669ffde9f17SJean Perier   // Try to gather the array constructor extent.
670ffde9f17SJean Perier   mlir::Value extent;
671ffde9f17SJean Perier   fir::SequenceType::Extent typeExtent = fir::SequenceType::getUnknownExtent();
6729683a9c9SJean Perier   auto shapeExpr = Fortran::evaluate::GetContextFreeShape(
6739683a9c9SJean Perier       converter.getFoldingContext(), arrayCtorExpr);
674ffde9f17SJean Perier   if (shapeExpr && shapeExpr->size() == 1 && (*shapeExpr)[0]) {
675ffde9f17SJean Perier     const Fortran::evaluate::ExtentExpr &extentExpr = *(*shapeExpr)[0];
676ffde9f17SJean Perier     if (auto constantExtent = Fortran::evaluate::ToInt64(extentExpr)) {
677ffde9f17SJean Perier       typeExtent = *constantExtent;
678ffde9f17SJean Perier       extent = builder.createIntegerConstant(loc, idxType, typeExtent);
679ffde9f17SJean Perier     } else if (isCallFreeExpr(extentExpr)) {
680ffde9f17SJean Perier       // The expression built by expression analysis for the array constructor
681ffde9f17SJean Perier       // extent does not contain procedure symbols. It is side effect free.
682ffde9f17SJean Perier       // This could be relaxed to allow pure procedure, but some care must
683ffde9f17SJean Perier       // be taken to not bring in "unmapped" symbols from callee scopes.
684ffde9f17SJean Perier       extent = lowerExtentExpr(loc, converter, symMap, stmtCtx, extentExpr);
685ffde9f17SJean Perier     }
686ffde9f17SJean Perier     // Otherwise, the temporary will have to be built step by step with
687ffde9f17SJean Perier     // reallocation and the extent will only be known at the end of the array
688ffde9f17SJean Perier     // constructor evaluation.
689ffde9f17SJean Perier   }
690ffde9f17SJean Perier   // Convert the array constructor type and try to gather its length parameter
691ffde9f17SJean Perier   // values, if any.
692ffde9f17SJean Perier   mlir::SmallVector<mlir::Value> lengths;
693ffde9f17SJean Perier   mlir::Type elementType = LengthAndTypeCollector<T>::collect(
694ffde9f17SJean Perier       loc, converter, arrayCtorExpr, symMap, stmtCtx, lengths);
695ffde9f17SJean Perier   // Run an analysis of the array constructor ac-value.
696a9e4bb38SJean Perier   ArrayCtorAnalysis analysis(converter.getFoldingContext(), arrayCtorExpr);
697ffde9f17SJean Perier   bool needToEvaluateOneExprToGetLengthParameters =
6989683a9c9SJean Perier       missingLengthParameters(elementType, lengths);
6999683a9c9SJean Perier   auto declaredType = fir::SequenceType::get({typeExtent}, elementType);
700ffde9f17SJean Perier 
701ffde9f17SJean Perier   // Based on what was gathered and the result of the analysis, select and
702ffde9f17SJean Perier   // instantiate the right lowering strategy for the array constructor.
703ffde9f17SJean Perier   if (!extent || needToEvaluateOneExprToGetLengthParameters ||
704fac349a1SChristian Sigg       analysis.anyArrayExpr ||
705fac349a1SChristian Sigg       mlir::isa<fir::RecordType>(declaredType.getEleTy()))
7069683a9c9SJean Perier     return RuntimeTempStrategy(
707ebae4cc7SSlava Zakharin         loc, builder, stmtCtx, symMap, declaredType,
7089683a9c9SJean Perier         extent ? std::optional<mlir::Value>(extent) : std::nullopt, lengths,
7099683a9c9SJean Perier         needToEvaluateOneExprToGetLengthParameters);
7107b4aa95dSSlava Zakharin   // Note: the generated hlfir.elemental is always unordered, thus,
7117b4aa95dSSlava Zakharin   // AsElementalStrategy can only be used for array constructors without
7127b4aa95dSSlava Zakharin   // impure ac-value expressions. If/when this changes, make sure
7137b4aa95dSSlava Zakharin   // the 'unordered' attribute is set accordingly for the hlfir.elemental.
714a9e4bb38SJean Perier   if (analysis.isSingleImpliedDoWithOneScalarPureExpr())
715ebae4cc7SSlava Zakharin     return AsElementalStrategy(loc, builder, stmtCtx, symMap, declaredType,
716ebae4cc7SSlava Zakharin                                extent, lengths);
717ffde9f17SJean Perier 
718ffde9f17SJean Perier   if (analysis.anyImpliedDo)
719ebae4cc7SSlava Zakharin     return InlinedTempStrategy(loc, builder, stmtCtx, symMap, declaredType,
720ebae4cc7SSlava Zakharin                                extent, lengths);
721ffde9f17SJean Perier 
722ebae4cc7SSlava Zakharin   return LooplessInlinedTempStrategy(loc, builder, stmtCtx, symMap,
723ebae4cc7SSlava Zakharin                                      declaredType, extent, lengths);
724ffde9f17SJean Perier }
725ffde9f17SJean Perier 
726ffde9f17SJean Perier /// Lower an ac-value expression \p expr and forward it to the selected
727ffde9f17SJean Perier /// lowering strategy \p arrayBuilder,
728ffde9f17SJean Perier template <typename T>
729ffde9f17SJean Perier static void genAcValue(mlir::Location loc,
730ffde9f17SJean Perier                        Fortran::lower::AbstractConverter &converter,
731ffde9f17SJean Perier                        const Fortran::evaluate::Expr<T> &expr,
732ffde9f17SJean Perier                        Fortran::lower::SymMap &symMap,
733ffde9f17SJean Perier                        Fortran::lower::StatementContext &stmtCtx,
734ffde9f17SJean Perier                        ArrayCtorLoweringStrategy &arrayBuilder) {
735ffde9f17SJean Perier   // TODO: get rid of the toEvExpr indirection.
736ffde9f17SJean Perier   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
737ffde9f17SJean Perier   hlfir::Entity value = Fortran::lower::convertExprToHLFIR(
738ffde9f17SJean Perier       loc, converter, toEvExpr(expr), symMap, stmtCtx);
739ffde9f17SJean Perier   value = hlfir::loadTrivialScalar(loc, builder, value);
740ffde9f17SJean Perier   arrayBuilder.pushValue(loc, builder, value);
741ffde9f17SJean Perier }
742ffde9f17SJean Perier 
743ffde9f17SJean Perier /// Lowers an ac-value implied-do \p impledDo according to the selected
744ffde9f17SJean Perier /// lowering strategy \p arrayBuilder.
745ffde9f17SJean Perier template <typename T>
746ffde9f17SJean Perier static void genAcValue(mlir::Location loc,
747ffde9f17SJean Perier                        Fortran::lower::AbstractConverter &converter,
748ffde9f17SJean Perier                        const Fortran::evaluate::ImpliedDo<T> &impledDo,
749ffde9f17SJean Perier                        Fortran::lower::SymMap &symMap,
750ffde9f17SJean Perier                        Fortran::lower::StatementContext &stmtCtx,
751ffde9f17SJean Perier                        ArrayCtorLoweringStrategy &arrayBuilder) {
752ffde9f17SJean Perier   auto lowerIndex =
753ffde9f17SJean Perier       [&](const Fortran::evaluate::ExtentExpr expr) -> mlir::Value {
754ffde9f17SJean Perier     return lowerExtentExpr(loc, converter, symMap, stmtCtx, expr);
755ffde9f17SJean Perier   };
756ffde9f17SJean Perier   mlir::Value lower = lowerIndex(impledDo.lower());
757ffde9f17SJean Perier   mlir::Value upper = lowerIndex(impledDo.upper());
758ffde9f17SJean Perier   mlir::Value stride = lowerIndex(impledDo.stride());
759ffde9f17SJean Perier   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
760ffde9f17SJean Perier   mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint();
761ffde9f17SJean Perier   mlir::Value impliedDoIndexValue =
762ffde9f17SJean Perier       arrayBuilder.startImpliedDo(loc, builder, lower, upper, stride);
763ebae4cc7SSlava Zakharin   arrayBuilder.startImpliedDoScope(toStringRef(impledDo.name()),
764ffde9f17SJean Perier                                    impliedDoIndexValue);
765ffde9f17SJean Perier 
766ffde9f17SJean Perier   for (const auto &acValue : impledDo.values())
76777d8cfb3SAlexander Shaposhnikov     Fortran::common::visit(
768ffde9f17SJean Perier         [&](const auto &x) {
769ffde9f17SJean Perier           genAcValue(loc, converter, x, symMap, stmtCtx, arrayBuilder);
770ffde9f17SJean Perier         },
771ffde9f17SJean Perier         acValue.u);
772ffde9f17SJean Perier 
773ebae4cc7SSlava Zakharin   arrayBuilder.endImpliedDoScope();
774ffde9f17SJean Perier   builder.restoreInsertionPoint(insertPt);
775ffde9f17SJean Perier }
776ffde9f17SJean Perier 
777ffde9f17SJean Perier /// Entry point for evaluate::ArrayConstructor lowering.
778ffde9f17SJean Perier template <typename T>
779ffde9f17SJean Perier hlfir::EntityWithAttributes Fortran::lower::ArrayConstructorBuilder<T>::gen(
780ffde9f17SJean Perier     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
781ffde9f17SJean Perier     const Fortran::evaluate::ArrayConstructor<T> &arrayCtorExpr,
782ffde9f17SJean Perier     Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
783ffde9f17SJean Perier   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
784ffde9f17SJean Perier   // Select the lowering strategy given the array constructor.
785ffde9f17SJean Perier   auto arrayBuilder = selectArrayCtorLoweringStrategy(
786ffde9f17SJean Perier       loc, converter, arrayCtorExpr, symMap, stmtCtx);
787ffde9f17SJean Perier   // Run the array lowering strategy through the ac-values.
788ffde9f17SJean Perier   for (const auto &acValue : arrayCtorExpr)
78977d8cfb3SAlexander Shaposhnikov     Fortran::common::visit(
790ffde9f17SJean Perier         [&](const auto &x) {
791ffde9f17SJean Perier           genAcValue(loc, converter, x, symMap, stmtCtx, arrayBuilder);
792ffde9f17SJean Perier         },
793ffde9f17SJean Perier         acValue.u);
794ffde9f17SJean Perier   hlfir::Entity hlfirExpr = arrayBuilder.finishArrayCtorLowering(loc, builder);
795ffde9f17SJean Perier   // Insert the clean-up for the created hlfir.expr.
796ffde9f17SJean Perier   fir::FirOpBuilder *bldr = &builder;
797ffde9f17SJean Perier   stmtCtx.attachCleanup(
798ffde9f17SJean Perier       [=]() { bldr->create<hlfir::DestroyOp>(loc, hlfirExpr); });
799ffde9f17SJean Perier   return hlfir::EntityWithAttributes{hlfirExpr};
800ffde9f17SJean Perier }
801ffde9f17SJean Perier 
802ffde9f17SJean Perier using namespace Fortran::evaluate;
803ffde9f17SJean Perier using namespace Fortran::common;
804ffde9f17SJean Perier FOR_EACH_SPECIFIC_TYPE(template class Fortran::lower::ArrayConstructorBuilder, )
805