1 //===- ConvertArrayConstructor.cpp -- Array Constructor ---------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "flang/Lower/ConvertArrayConstructor.h" 10 #include "flang/Evaluate/expression.h" 11 #include "flang/Lower/AbstractConverter.h" 12 #include "flang/Lower/ConvertExprToHLFIR.h" 13 #include "flang/Lower/ConvertType.h" 14 #include "flang/Lower/StatementContext.h" 15 #include "flang/Lower/SymbolMap.h" 16 #include "flang/Optimizer/Builder/HLFIRTools.h" 17 #include "flang/Optimizer/Builder/Todo.h" 18 #include "flang/Optimizer/HLFIR/HLFIROps.h" 19 20 // Array constructors are lowered with three different strategies. 21 // All strategies are not possible with all array constructors. 22 // 23 // - Strategy 1: runtime approach (RuntimeTempStrategy). 24 // This strategy works will all array constructors, but will create more 25 // complex code that is harder to optimize. An allocatable temp is created, 26 // it may be unallocated if the array constructor length parameters or extent 27 // could not be computed. Then, the runtime is called to push lowered 28 // ac-value (array constructor elements) into the allocatable. The runtime 29 // will allocate or reallocate as needed while values are being pushed. 30 // In the end, the allocatable contain a temporary with all the array 31 // constructor evaluated elements. 32 // 33 // - Strategy 2: inlined temporary approach (InlinedTempStrategyImpl) 34 // This strategy can only be used if the array constructor extent and length 35 // parameters can be pre-computed without evaluating any ac-value, and if all 36 // of the ac-value are scalars (at least for now). 37 // A temporary is allocated inline in one go, and an index pointing at the 38 // current ac-value position in the array constructor element sequence is 39 // maintained and used to store ac-value as they are being lowered. 40 // 41 // - Strategy 3: "function of the indices" approach (AsElementalStrategy) 42 // This strategy can only be used if the array constructor extent and length 43 // parameters can be pre-computed and, if the array constructor is of the 44 // form "[(scalar_expr, ac-implied-do-control)]". In this case, it is lowered 45 // into an hlfir.elemental without creating any temporary in lowering. This 46 // form should maximize the chance of array temporary elision when assigning 47 // the array constructor, potentially reshaped, to an array variable. 48 // 49 // The array constructor lowering looks like: 50 // ``` 51 // strategy = selectArrayCtorLoweringStrategy(array-ctor-expr); 52 // for (ac-value : array-ctor-expr) 53 // if (ac-value is expression) { 54 // strategy.pushValue(ac-value); 55 // } else if (ac-value is implied-do) { 56 // strategy.startImpliedDo(lower, upper, stride); 57 // // lower nested values 58 // } 59 // result = strategy.finishArrayCtorLowering(); 60 // ``` 61 62 //===----------------------------------------------------------------------===// 63 // Definition of the lowering strategies. Each lowering strategy is defined 64 // as a class that implements "pushValue", "startImpliedDo", and 65 // "finishArrayCtorLowering". 66 //===----------------------------------------------------------------------===// 67 68 namespace { 69 /// Class that implements the "inlined temp strategy" to lower array 70 /// constructors. It must be further provided a CounterType class to specify how 71 /// the current ac-value insertion position is tracked. 72 template <typename CounterType> 73 class InlinedTempStrategyImpl { 74 /// Name that will be given to the temporary allocation and hlfir.declare in 75 /// the IR. 76 static constexpr char tempName[] = ".tmp.arrayctor"; 77 78 public: 79 /// Start lowering an array constructor according to the inline strategy. 80 /// The temporary is created right away. 81 InlinedTempStrategyImpl(mlir::Location loc, fir::FirOpBuilder &builder, 82 fir::SequenceType declaredType, mlir::Value extent, 83 llvm::ArrayRef<mlir::Value> lengths) 84 : one{builder.createIntegerConstant(loc, builder.getIndexType(), 1)}, 85 counter{loc, builder, one} { 86 // Allocate the temporary storage. 87 llvm::SmallVector<mlir::Value, 1> extents{extent}; 88 mlir::Value tempStorage = builder.createHeapTemporary( 89 loc, declaredType, tempName, extents, lengths); 90 mlir::Value shape = builder.genShape(loc, extents); 91 temp = 92 builder 93 .create<hlfir::DeclareOp>(loc, tempStorage, tempName, shape, 94 lengths, fir::FortranVariableFlagsAttr{}) 95 .getBase(); 96 } 97 98 /// Push a lowered ac-value into the current insertion point and 99 /// increment the insertion point. 100 void pushValue(mlir::Location loc, fir::FirOpBuilder &builder, 101 hlfir::Entity value) { 102 assert(value.isScalar() && "cannot use inlined temp with array values"); 103 mlir::Value indexValue = counter.getAndIncrementIndex(loc, builder, one); 104 hlfir::Entity tempElement = hlfir::getElementAt( 105 loc, builder, hlfir::Entity{temp}, mlir::ValueRange{indexValue}); 106 // TODO: "copy" would probably be better than assign to ensure there are no 107 // side effects (user assignments, temp, lhs finalization)? 108 // This only makes a difference for derived types, so for now derived types 109 // will use the runtime strategy to avoid any bad behaviors. 110 builder.create<hlfir::AssignOp>(loc, value, tempElement); 111 } 112 113 /// Start a fir.do_loop with the control from an implied-do and return 114 /// the loop induction variable that is the ac-do-variable value. 115 /// Only usable if the counter is able to track the position through loops. 116 mlir::Value startImpliedDo(mlir::Location loc, fir::FirOpBuilder &builder, 117 mlir::Value lower, mlir::Value upper, 118 mlir::Value stride) { 119 if constexpr (!CounterType::canCountThroughLoops) 120 fir::emitFatalError(loc, "array constructor lowering is inconsistent"); 121 auto loop = builder.create<fir::DoLoopOp>(loc, lower, upper, stride, 122 /*unordered=*/false, 123 /*finalCount=*/false); 124 builder.setInsertionPointToStart(loop.getBody()); 125 return loop.getInductionVar(); 126 } 127 128 /// Move the temporary to an hlfir.expr value (array constructors are not 129 /// variables and cannot be further modified). 130 hlfir::Entity finishArrayCtorLowering(mlir::Location loc, 131 fir::FirOpBuilder &builder) { 132 // Temp is created using createHeapTemporary. 133 mlir::Value mustFree = builder.createBool(loc, true); 134 auto hlfirExpr = builder.create<hlfir::AsExprOp>(loc, temp, mustFree); 135 return hlfir::Entity{hlfirExpr}; 136 } 137 138 private: 139 mlir::Value one; 140 CounterType counter; 141 mlir::Value temp; 142 }; 143 144 /// A simple SSA value counter to lower array constructors without any 145 /// implied-do in the "inlined temp strategy". 146 /// The SSA value being tracked by the counter (hence, this 147 /// cannot count through loops since the SSA value in the loop becomes 148 /// inaccessible after the loop). 149 /// Semantic analysis expression rewrites unroll implied do loop with 150 /// compile time constant bounds (even if huge). So this minimalistic 151 /// counter greatly reduces the generated IR for simple but big array 152 /// constructors [(i,i=1,constant-expr)] that are expected to be quite 153 /// common. 154 class ValueCounter { 155 public: 156 static constexpr bool canCountThroughLoops = false; 157 ValueCounter(mlir::Location loc, fir::FirOpBuilder &builder, 158 mlir::Value initialValue) { 159 indexValue = initialValue; 160 } 161 162 mlir::Value getAndIncrementIndex(mlir::Location loc, 163 fir::FirOpBuilder &builder, 164 mlir::Value increment) { 165 mlir::Value currentValue = indexValue; 166 indexValue = 167 builder.create<mlir::arith::AddIOp>(loc, indexValue, increment); 168 return currentValue; 169 } 170 171 private: 172 mlir::Value indexValue; 173 }; 174 using LooplessInlinedTempStrategy = InlinedTempStrategyImpl<ValueCounter>; 175 176 /// A generic memory based counter that can deal with all cases of 177 /// "inlined temp strategy". The counter value is stored in a temp 178 /// from which it is loaded, incremented, and stored every time an 179 /// ac-value is pushed. 180 class InMemoryCounter { 181 public: 182 static constexpr bool canCountThroughLoops = true; 183 InMemoryCounter(mlir::Location loc, fir::FirOpBuilder &builder, 184 mlir::Value initialValue) { 185 indexVar = builder.createTemporary(loc, initialValue.getType()); 186 builder.create<fir::StoreOp>(loc, initialValue, indexVar); 187 } 188 189 mlir::Value getAndIncrementIndex(mlir::Location loc, 190 fir::FirOpBuilder &builder, 191 mlir::Value increment) const { 192 mlir::Value indexValue = builder.create<fir::LoadOp>(loc, indexVar); 193 indexValue = 194 builder.create<mlir::arith::AddIOp>(loc, indexValue, increment); 195 builder.create<fir::StoreOp>(loc, indexValue, indexVar); 196 return indexValue; 197 } 198 199 private: 200 mlir::Value indexVar; 201 }; 202 using InlinedTempStrategy = InlinedTempStrategyImpl<InMemoryCounter>; 203 204 // TODO: add and implement AsElementalStrategy. 205 206 // TODO: add and implement RuntimeTempStrategy. 207 208 /// Wrapper class that dispatch to the selected array constructor lowering 209 /// strategy and does nothing else. 210 class ArrayCtorLoweringStrategy { 211 public: 212 template <typename A> 213 ArrayCtorLoweringStrategy(A &&impl) : implVariant{std::forward<A>(impl)} {} 214 215 void pushValue(mlir::Location loc, fir::FirOpBuilder &builder, 216 hlfir::Entity value) { 217 return std::visit( 218 [&](auto &impl) { return impl.pushValue(loc, builder, value); }, 219 implVariant); 220 } 221 222 mlir::Value startImpliedDo(mlir::Location loc, fir::FirOpBuilder &builder, 223 mlir::Value lower, mlir::Value upper, 224 mlir::Value stride) { 225 return std::visit( 226 [&](auto &impl) { 227 return impl.startImpliedDo(loc, builder, lower, upper, stride); 228 }, 229 implVariant); 230 } 231 232 hlfir::Entity finishArrayCtorLowering(mlir::Location loc, 233 fir::FirOpBuilder &builder) { 234 return std::visit( 235 [&](auto &impl) { return impl.finishArrayCtorLowering(loc, builder); }, 236 implVariant); 237 } 238 239 private: 240 std::variant<InlinedTempStrategy, LooplessInlinedTempStrategy> implVariant; 241 }; 242 } // namespace 243 244 //===----------------------------------------------------------------------===// 245 // Definition of selectArrayCtorLoweringStrategy and its helpers. 246 // This is the code that analyses the evaluate::ArrayConstructor<T>, 247 // pre-lowers the array constructor extent and length parameters if it can, 248 // and chooses the lowering strategy. 249 //===----------------------------------------------------------------------===// 250 251 namespace { 252 /// Helper class to lower the array constructor type and its length parameters. 253 /// The length parameters, if any, are only lowered if this does not require 254 /// evaluating an ac-value. 255 template <typename T> 256 struct LengthAndTypeCollector { 257 static mlir::Type collect(mlir::Location, 258 Fortran::lower::AbstractConverter &converter, 259 const Fortran::evaluate::ArrayConstructor<T> &, 260 Fortran::lower::SymMap &, 261 Fortran::lower::StatementContext &, 262 mlir::SmallVectorImpl<mlir::Value> &) { 263 // Numerical and Logical types. 264 return Fortran::lower::getFIRType(&converter.getMLIRContext(), T::category, 265 T::kind, /*lenParams*/ {}); 266 } 267 }; 268 269 template <> 270 struct LengthAndTypeCollector<Fortran::evaluate::SomeDerived> { 271 static mlir::Type collect( 272 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 273 const Fortran::evaluate::ArrayConstructor<Fortran::evaluate::SomeDerived> 274 &arrayCtorExpr, 275 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx, 276 mlir::SmallVectorImpl<mlir::Value> &lengths) { 277 TODO(loc, "collect derived type and length"); 278 } 279 }; 280 281 template <int Kind> 282 using Character = 283 Fortran::evaluate::Type<Fortran::common::TypeCategory::Character, Kind>; 284 template <int Kind> 285 struct LengthAndTypeCollector<Character<Kind>> { 286 static mlir::Type collect( 287 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 288 const Fortran::evaluate::ArrayConstructor<Character<Kind>> &arrayCtorExpr, 289 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx, 290 mlir::SmallVectorImpl<mlir::Value> &lengths) { 291 TODO(loc, "collect character type and length"); 292 } 293 }; 294 } // namespace 295 296 /// Does the array constructor have length parameters that 297 /// LengthAndTypeCollector::collect could not lower because this requires 298 /// lowering an ac-value and must be delayed? 299 static bool 300 failedToGatherLengthParameters(mlir::Type elementType, 301 llvm::ArrayRef<mlir::Value> lengths) { 302 return (elementType.isa<fir::CharacterType>() || 303 fir::isRecordWithTypeParameters(elementType)) && 304 lengths.empty(); 305 } 306 307 namespace { 308 /// Structure that analyses the ac-value and implied-do of 309 /// evaluate::ArrayConstructor before they are lowered. It does not generate any 310 /// IR. The result of this analysis pass is used to select the lowering 311 /// strategy. 312 struct ArrayCtorAnalysis { 313 template <typename T> 314 ArrayCtorAnalysis( 315 const Fortran::evaluate::ArrayConstructor<T> &arrayCtorExpr); 316 317 // Can the array constructor easily be rewritten into an hlfir.elemental ? 318 bool isSingleImpliedDoWithOneScalarExpr() const { 319 return !anyArrayExpr && isPerfectLoopNest && 320 innerNumberOfExprIfPrefectNest == 1 && depthIfPerfectLoopNest == 1; 321 } 322 323 bool anyImpliedDo{false}; 324 bool anyArrayExpr{false}; 325 bool isPerfectLoopNest{true}; 326 std::int64_t innerNumberOfExprIfPrefectNest = 0; 327 std::int64_t depthIfPerfectLoopNest = 0; 328 }; 329 } // namespace 330 331 template <typename T> 332 ArrayCtorAnalysis::ArrayCtorAnalysis( 333 const Fortran::evaluate::ArrayConstructor<T> &arrayCtorExpr) { 334 llvm::SmallVector<const Fortran::evaluate::ArrayConstructorValues<T> *> 335 arrayValueListStack{&arrayCtorExpr}; 336 // Loop through the ac-value-list(s) of the array constructor. 337 while (!arrayValueListStack.empty()) { 338 std::int64_t localNumberOfImpliedDo = 0; 339 std::int64_t localNumberOfExpr = 0; 340 // Loop though the ac-value of an ac-value list, and add any nested 341 // ac-value-list of ac-implied-do to the stack. 342 for (const Fortran::evaluate::ArrayConstructorValue<T> &acValue : 343 *arrayValueListStack.pop_back_val()) 344 std::visit(Fortran::common::visitors{ 345 [&](const Fortran::evaluate::ImpliedDo<T> &impledDo) { 346 arrayValueListStack.push_back(&impledDo.values()); 347 localNumberOfImpliedDo++; 348 }, 349 [&](const Fortran::evaluate::Expr<T> &expr) { 350 localNumberOfExpr++; 351 anyArrayExpr = anyArrayExpr || expr.Rank() > 0; 352 }}, 353 acValue.u); 354 anyImpliedDo = anyImpliedDo || localNumberOfImpliedDo > 0; 355 356 if (localNumberOfImpliedDo == 0) { 357 // Leaf ac-value-list in the array constructor ac-value tree. 358 if (isPerfectLoopNest) 359 // This this the only leaf of the array-constructor (the array 360 // constructor is a nest of single implied-do with a list of expression 361 // in the last deeper implied do). e.g: "[((i+j, i=1,n)j=1,m)]". 362 innerNumberOfExprIfPrefectNest = localNumberOfExpr; 363 } else if (localNumberOfImpliedDo == 1 && localNumberOfExpr == 0) { 364 // Perfect implied-do nest new level. 365 ++depthIfPerfectLoopNest; 366 } else { 367 // More than one implied-do, or at least one implied-do and an expr 368 // at that level. This will not form a perfect nest. Examples: 369 // "[a, (i, i=1,n)]" or "[(i, i=1,n), (j, j=1,m)]". 370 isPerfectLoopNest = false; 371 } 372 } 373 } 374 375 /// Helper to lower a scalar extent expression (like implied-do bounds). 376 static mlir::Value lowerExtentExpr(mlir::Location loc, 377 Fortran::lower::AbstractConverter &converter, 378 Fortran::lower::SymMap &symMap, 379 Fortran::lower::StatementContext &stmtCtx, 380 const Fortran::evaluate::ExtentExpr &expr) { 381 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 382 mlir::IndexType idxTy = builder.getIndexType(); 383 hlfir::Entity value = Fortran::lower::convertExprToHLFIR( 384 loc, converter, toEvExpr(expr), symMap, stmtCtx); 385 value = hlfir::loadTrivialScalar(loc, builder, value); 386 return builder.createConvert(loc, idxTy, value); 387 } 388 389 /// Does \p expr contain no calls to user function? 390 static bool isCallFreeExpr(const Fortran::evaluate::ExtentExpr &expr) { 391 for (const Fortran::semantics::Symbol &symbol : 392 Fortran::evaluate::CollectSymbols(expr)) 393 if (Fortran::semantics::IsProcedure(symbol)) 394 return false; 395 return true; 396 } 397 398 /// Core function that pre-lowers the extent and length parameters of 399 /// array constructors if it can, runs the ac-value analysis and 400 /// select the lowering strategy accordingly. 401 template <typename T> 402 static ArrayCtorLoweringStrategy selectArrayCtorLoweringStrategy( 403 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 404 const Fortran::evaluate::ArrayConstructor<T> &arrayCtorExpr, 405 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { 406 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 407 mlir::Type idxType = builder.getIndexType(); 408 // Try to gather the array constructor extent. 409 mlir::Value extent; 410 fir::SequenceType::Extent typeExtent = fir::SequenceType::getUnknownExtent(); 411 auto shapeExpr = 412 Fortran::evaluate::GetShape(converter.getFoldingContext(), arrayCtorExpr); 413 if (shapeExpr && shapeExpr->size() == 1 && (*shapeExpr)[0]) { 414 const Fortran::evaluate::ExtentExpr &extentExpr = *(*shapeExpr)[0]; 415 if (auto constantExtent = Fortran::evaluate::ToInt64(extentExpr)) { 416 typeExtent = *constantExtent; 417 extent = builder.createIntegerConstant(loc, idxType, typeExtent); 418 } else if (isCallFreeExpr(extentExpr)) { 419 // The expression built by expression analysis for the array constructor 420 // extent does not contain procedure symbols. It is side effect free. 421 // This could be relaxed to allow pure procedure, but some care must 422 // be taken to not bring in "unmapped" symbols from callee scopes. 423 extent = lowerExtentExpr(loc, converter, symMap, stmtCtx, extentExpr); 424 } 425 // Otherwise, the temporary will have to be built step by step with 426 // reallocation and the extent will only be known at the end of the array 427 // constructor evaluation. 428 } 429 // Convert the array constructor type and try to gather its length parameter 430 // values, if any. 431 mlir::SmallVector<mlir::Value> lengths; 432 mlir::Type elementType = LengthAndTypeCollector<T>::collect( 433 loc, converter, arrayCtorExpr, symMap, stmtCtx, lengths); 434 // Run an analysis of the array constructor ac-value. 435 ArrayCtorAnalysis analysis(arrayCtorExpr); 436 bool needToEvaluateOneExprToGetLengthParameters = 437 failedToGatherLengthParameters(elementType, lengths); 438 439 // Based on what was gathered and the result of the analysis, select and 440 // instantiate the right lowering strategy for the array constructor. 441 if (!extent || needToEvaluateOneExprToGetLengthParameters || 442 analysis.anyArrayExpr) 443 TODO(loc, "Lowering of array constructor requiring the runtime"); 444 445 auto declaredType = fir::SequenceType::get({typeExtent}, elementType); 446 if (analysis.isSingleImpliedDoWithOneScalarExpr()) 447 TODO(loc, "Lowering of array constructor as hlfir.elemental"); 448 449 if (analysis.anyImpliedDo) 450 return InlinedTempStrategy(loc, builder, declaredType, extent, lengths); 451 452 return LooplessInlinedTempStrategy(loc, builder, declaredType, extent, 453 lengths); 454 } 455 456 /// Lower an ac-value expression \p expr and forward it to the selected 457 /// lowering strategy \p arrayBuilder, 458 template <typename T> 459 static void genAcValue(mlir::Location loc, 460 Fortran::lower::AbstractConverter &converter, 461 const Fortran::evaluate::Expr<T> &expr, 462 Fortran::lower::SymMap &symMap, 463 Fortran::lower::StatementContext &stmtCtx, 464 ArrayCtorLoweringStrategy &arrayBuilder) { 465 if (expr.Rank() != 0) 466 TODO(loc, "array constructor with array ac-value in HLFIR"); 467 // TODO: get rid of the toEvExpr indirection. 468 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 469 hlfir::Entity value = Fortran::lower::convertExprToHLFIR( 470 loc, converter, toEvExpr(expr), symMap, stmtCtx); 471 value = hlfir::loadTrivialScalar(loc, builder, value); 472 arrayBuilder.pushValue(loc, builder, value); 473 } 474 475 /// Lowers an ac-value implied-do \p impledDo according to the selected 476 /// lowering strategy \p arrayBuilder. 477 template <typename T> 478 static void genAcValue(mlir::Location loc, 479 Fortran::lower::AbstractConverter &converter, 480 const Fortran::evaluate::ImpliedDo<T> &impledDo, 481 Fortran::lower::SymMap &symMap, 482 Fortran::lower::StatementContext &stmtCtx, 483 ArrayCtorLoweringStrategy &arrayBuilder) { 484 auto lowerIndex = 485 [&](const Fortran::evaluate::ExtentExpr expr) -> mlir::Value { 486 return lowerExtentExpr(loc, converter, symMap, stmtCtx, expr); 487 }; 488 mlir::Value lower = lowerIndex(impledDo.lower()); 489 mlir::Value upper = lowerIndex(impledDo.upper()); 490 mlir::Value stride = lowerIndex(impledDo.stride()); 491 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 492 mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint(); 493 mlir::Value impliedDoIndexValue = 494 arrayBuilder.startImpliedDo(loc, builder, lower, upper, stride); 495 symMap.pushImpliedDoBinding(toStringRef(impledDo.name()), 496 impliedDoIndexValue); 497 stmtCtx.pushScope(); 498 499 for (const auto &acValue : impledDo.values()) 500 std::visit( 501 [&](const auto &x) { 502 genAcValue(loc, converter, x, symMap, stmtCtx, arrayBuilder); 503 }, 504 acValue.u); 505 506 stmtCtx.finalizeAndPop(); 507 symMap.popImpliedDoBinding(); 508 builder.restoreInsertionPoint(insertPt); 509 } 510 511 /// Entry point for evaluate::ArrayConstructor lowering. 512 template <typename T> 513 hlfir::EntityWithAttributes Fortran::lower::ArrayConstructorBuilder<T>::gen( 514 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 515 const Fortran::evaluate::ArrayConstructor<T> &arrayCtorExpr, 516 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { 517 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 518 // Select the lowering strategy given the array constructor. 519 auto arrayBuilder = selectArrayCtorLoweringStrategy( 520 loc, converter, arrayCtorExpr, symMap, stmtCtx); 521 // Run the array lowering strategy through the ac-values. 522 for (const auto &acValue : arrayCtorExpr) 523 std::visit( 524 [&](const auto &x) { 525 genAcValue(loc, converter, x, symMap, stmtCtx, arrayBuilder); 526 }, 527 acValue.u); 528 hlfir::Entity hlfirExpr = arrayBuilder.finishArrayCtorLowering(loc, builder); 529 // Insert the clean-up for the created hlfir.expr. 530 fir::FirOpBuilder *bldr = &builder; 531 stmtCtx.attachCleanup( 532 [=]() { bldr->create<hlfir::DestroyOp>(loc, hlfirExpr); }); 533 return hlfir::EntityWithAttributes{hlfirExpr}; 534 } 535 536 using namespace Fortran::evaluate; 537 using namespace Fortran::common; 538 FOR_EACH_SPECIFIC_TYPE(template class Fortran::lower::ArrayConstructorBuilder, ) 539