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