[flang-commits] [flang] ffde9f1 - [flang][hlfir] Array constructor lowering [part 1/4]

Jean Perier via flang-commits flang-commits at lists.llvm.org
Thu Feb 16 06:20:13 PST 2023


Author: Jean Perier
Date: 2023-02-16T15:18:55+01:00
New Revision: ffde9f17300b52c9b53311455b8991cbfc6da377

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

LOG: [flang][hlfir] Array constructor lowering [part 1/4]

This is the first and biggest chunk that introduces support for
array constructor to HLFIR.

This patch:
- adds a new ConvertArrayConstructor.cpp that centralizes the
  code dealing with array constructor lowering.
- introduces a framework to lower array constructor according to
  different strategies: A common analysis of the array constructor is
  done, and based on that, a lowering startegy is selected and driven
  through the ac-values of the array constructor. See
  ConvertArrayConstructor.cpp comments for more details.
- implements the first strategy that creates a temporary inlined and
  updates it with inlined code. This strategy can only be used if the
  temporary can be pre-allocated (i.e: the extents and length parameters
  can be pre-computed without evaluating any ac-values), and if all the
  ac-value expressions are scalars.

For the sake of simplicity, characters and derived type will be enabled
once all the strategies are added.

Reviewed By: clementval, PeteSteinfeld

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

Added: 
    flang/include/flang/Lower/ConvertArrayConstructor.h
    flang/lib/Lower/ConvertArrayConstructor.cpp
    flang/test/Lower/HLFIR/array-ctor-as-inlined-temp.f90

Modified: 
    flang/lib/Lower/CMakeLists.txt
    flang/lib/Lower/ConvertExprToHLFIR.cpp

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Lower/ConvertArrayConstructor.h b/flang/include/flang/Lower/ConvertArrayConstructor.h
new file mode 100644
index 0000000000000..69cad2a526689
--- /dev/null
+++ b/flang/include/flang/Lower/ConvertArrayConstructor.h
@@ -0,0 +1,46 @@
+//===-- ConvertArrayConstructor.h -- Array constructor lowering -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
+//
+//===----------------------------------------------------------------------===//
+///
+/// Implements the conversion from evaluate::ArrayConstructor to HLFIR.
+///
+//===----------------------------------------------------------------------===//
+#ifndef FORTRAN_LOWER_CONVERTARRAYCONSTRUCTOR_H
+#define FORTRAN_LOWER_CONVERTARRAYCONSTRUCTOR_H
+
+#include "flang/Evaluate/type.h"
+#include "flang/Optimizer/Builder/HLFIRTools.h"
+
+namespace Fortran::evaluate {
+template <typename T>
+class ArrayConstructor;
+}
+
+namespace Fortran::lower {
+class AbstractConverter;
+class SymMap;
+class StatementContext;
+
+/// Class to lower evaluate::ArrayConstructor<T> to hlfir::EntityWithAttributes.
+template <typename T>
+class ArrayConstructorBuilder {
+public:
+  static hlfir::EntityWithAttributes
+  gen(mlir::Location loc, Fortran::lower::AbstractConverter &converter,
+      const Fortran::evaluate::ArrayConstructor<T> &expr,
+      Fortran::lower::SymMap &symMap,
+      Fortran::lower::StatementContext &stmtCtx);
+};
+using namespace evaluate;
+FOR_EACH_SPECIFIC_TYPE(extern template class ArrayConstructorBuilder, )
+} // namespace Fortran::lower
+
+#endif // FORTRAN_LOWER_CONVERTARRAYCONSTRUCTOR_H

diff  --git a/flang/lib/Lower/CMakeLists.txt b/flang/lib/Lower/CMakeLists.txt
index d4a12edfc56bc..7d2264c3ed8fb 100644
--- a/flang/lib/Lower/CMakeLists.txt
+++ b/flang/lib/Lower/CMakeLists.txt
@@ -5,6 +5,7 @@ add_flang_library(FortranLower
   Bridge.cpp
   CallInterface.cpp
   Coarray.cpp
+  ConvertArrayConstructor.cpp
   ConvertCall.cpp
   ConvertConstant.cpp
   ConvertExpr.cpp

diff  --git a/flang/lib/Lower/ConvertArrayConstructor.cpp b/flang/lib/Lower/ConvertArrayConstructor.cpp
new file mode 100644
index 0000000000000..2343a8a2edc77
--- /dev/null
+++ b/flang/lib/Lower/ConvertArrayConstructor.cpp
@@ -0,0 +1,538 @@
+//===- ConvertArrayConstructor.cpp -- Array Constructor ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Lower/ConvertArrayConstructor.h"
+#include "flang/Evaluate/expression.h"
+#include "flang/Lower/AbstractConverter.h"
+#include "flang/Lower/ConvertExprToHLFIR.h"
+#include "flang/Lower/ConvertType.h"
+#include "flang/Lower/StatementContext.h"
+#include "flang/Lower/SymbolMap.h"
+#include "flang/Optimizer/Builder/HLFIRTools.h"
+#include "flang/Optimizer/Builder/Todo.h"
+#include "flang/Optimizer/HLFIR/HLFIROps.h"
+
+// Array constructors are lowered with three 
diff erent strategies.
+// All strategies are not possible with all array constructors.
+//
+// - Strategy 1: runtime approach (RuntimeTempStrategy).
+//   This strategy works will all array constructors, but will create more
+//   complex code that is harder to optimize. An allocatable temp is created,
+//   it may be unallocated if the array constructor length parameters or extent
+//   could not be computed. Then, the runtime is called to push lowered
+//   ac-value (array constructor elements) into the allocatable. The runtime
+//   will allocate or reallocate as needed while values are being pushed.
+//   In the end, the allocatable contain a temporary with all the array
+//   constructor evaluated elements.
+//
+// - Strategy 2: inlined temporary approach (InlinedTempStrategyImpl)
+//   This strategy can only be used if the array constructor extent and length
+//   parameters can be pre-computed without evaluating any ac-value, and if all
+//   of the ac-value are scalars (at least for now).
+//   A temporary is allocated inline in one go, and an index pointing at the
+//   current ac-value position in the array constructor element sequence is
+//   maintained and used to store ac-value as they are being lowered.
+//
+// - Strategy 3: "function of the indices" approach (AsElementalStrategy)
+//   This strategy can only be used if the array constructor extent and length
+//   parameters can be pre-computed and, if the array constructor is of the
+//   form "[(scalar_expr, ac-implied-do-control)]". In this case, it is lowered
+//   into an hlfir.elemental without creating any temporary in lowering. This
+//   form should maximize the chance of array temporary elision when assigning
+//   the array constructor, potentially reshaped, to an array variable.
+//
+//   The array constructor lowering looks like:
+//   ```
+//     strategy = selectArrayCtorLoweringStrategy(array-ctor-expr);
+//     for (ac-value : array-ctor-expr)
+//       if (ac-value is expression) {
+//         strategy.pushValue(ac-value);
+//       } else if (ac-value is implied-do) {
+//         strategy.startImpliedDo(lower, upper, stride);
+//         // lower nested values
+//       }
+//     result = strategy.finishArrayCtorLowering();
+//   ```
+
+//===----------------------------------------------------------------------===//
+//   Definition of the lowering strategies. Each lowering strategy is defined
+//   as a class that implements "pushValue", "startImpliedDo", and
+//   "finishArrayCtorLowering".
+//===----------------------------------------------------------------------===//
+
+namespace {
+/// Class that implements the "inlined temp strategy" to lower array
+/// constructors. It must be further provided a CounterType class to specify how
+/// the current ac-value insertion position is tracked.
+template <typename CounterType>
+class InlinedTempStrategyImpl {
+  /// Name that will be given to the temporary allocation and hlfir.declare in
+  /// the IR.
+  static constexpr char tempName[] = ".tmp.arrayctor";
+
+public:
+  /// Start lowering an array constructor according to the inline strategy.
+  /// The temporary is created right away.
+  InlinedTempStrategyImpl(mlir::Location loc, fir::FirOpBuilder &builder,
+                          fir::SequenceType declaredType, mlir::Value extent,
+                          llvm::ArrayRef<mlir::Value> lengths)
+      : one{builder.createIntegerConstant(loc, builder.getIndexType(), 1)},
+        counter{loc, builder, one} {
+    // Allocate the temporary storage.
+    llvm::SmallVector<mlir::Value, 1> extents{extent};
+    mlir::Value tempStorage = builder.createHeapTemporary(
+        loc, declaredType, tempName, extents, lengths);
+    mlir::Value shape = builder.genShape(loc, extents);
+    temp =
+        builder
+            .create<hlfir::DeclareOp>(loc, tempStorage, tempName, shape,
+                                      lengths, fir::FortranVariableFlagsAttr{})
+            .getBase();
+  }
+
+  /// Push a lowered ac-value into the current insertion point and
+  /// increment the insertion point.
+  void pushValue(mlir::Location loc, fir::FirOpBuilder &builder,
+                 hlfir::Entity value) {
+    assert(value.isScalar() && "cannot use inlined temp with array values");
+    mlir::Value indexValue = counter.getAndIncrementIndex(loc, builder, one);
+    hlfir::Entity tempElement = hlfir::getElementAt(
+        loc, builder, hlfir::Entity{temp}, mlir::ValueRange{indexValue});
+    // TODO: "copy" would probably be better than assign to ensure there are no
+    // side effects (user assignments, temp, lhs finalization)?
+    // This only makes a 
diff erence for derived types, so for now derived types
+    // will use the runtime strategy to avoid any bad behaviors.
+    builder.create<hlfir::AssignOp>(loc, value, tempElement);
+  }
+
+  /// Start a fir.do_loop with the control from an implied-do and return
+  /// the loop induction variable that is the ac-do-variable value.
+  /// Only usable if the counter is able to track the position through loops.
+  mlir::Value startImpliedDo(mlir::Location loc, fir::FirOpBuilder &builder,
+                             mlir::Value lower, mlir::Value upper,
+                             mlir::Value stride) {
+    if constexpr (!CounterType::canCountThroughLoops)
+      fir::emitFatalError(loc, "array constructor lowering is inconsistent");
+    auto loop = builder.create<fir::DoLoopOp>(loc, lower, upper, stride,
+                                              /*unordered=*/false,
+                                              /*finalCount=*/false);
+    builder.setInsertionPointToStart(loop.getBody());
+    return loop.getInductionVar();
+  }
+
+  /// Move the temporary to an hlfir.expr value (array constructors are not
+  /// variables and cannot be further modified).
+  hlfir::Entity finishArrayCtorLowering(mlir::Location loc,
+                                        fir::FirOpBuilder &builder) {
+    // Temp is created using createHeapTemporary.
+    mlir::Value mustFree = builder.createBool(loc, true);
+    auto hlfirExpr = builder.create<hlfir::AsExprOp>(loc, temp, mustFree);
+    return hlfir::Entity{hlfirExpr};
+  }
+
+private:
+  mlir::Value one;
+  CounterType counter;
+  mlir::Value temp;
+};
+
+/// A simple SSA value counter to lower array constructors without any
+/// implied-do in the "inlined temp strategy".
+/// The SSA value being tracked by the counter (hence, this
+/// cannot count through loops since the SSA value in the loop becomes
+/// inaccessible after the loop).
+/// Semantic analysis expression rewrites unroll implied do loop with
+/// compile time constant bounds (even if huge). So this minimalistic
+/// counter greatly reduces the generated IR for simple but big array
+/// constructors [(i,i=1,constant-expr)] that are expected to be quite
+/// common.
+class ValueCounter {
+public:
+  static constexpr bool canCountThroughLoops = false;
+  ValueCounter(mlir::Location loc, fir::FirOpBuilder &builder,
+               mlir::Value initialValue) {
+    indexValue = initialValue;
+  }
+
+  mlir::Value getAndIncrementIndex(mlir::Location loc,
+                                   fir::FirOpBuilder &builder,
+                                   mlir::Value increment) {
+    mlir::Value currentValue = indexValue;
+    indexValue =
+        builder.create<mlir::arith::AddIOp>(loc, indexValue, increment);
+    return currentValue;
+  }
+
+private:
+  mlir::Value indexValue;
+};
+using LooplessInlinedTempStrategy = InlinedTempStrategyImpl<ValueCounter>;
+
+/// A generic memory based counter that can deal with all cases of
+/// "inlined temp strategy". The counter value is stored in a temp
+/// from which it is loaded, incremented, and stored every time an
+/// ac-value is pushed.
+class InMemoryCounter {
+public:
+  static constexpr bool canCountThroughLoops = true;
+  InMemoryCounter(mlir::Location loc, fir::FirOpBuilder &builder,
+                  mlir::Value initialValue) {
+    indexVar = builder.createTemporary(loc, initialValue.getType());
+    builder.create<fir::StoreOp>(loc, initialValue, indexVar);
+  }
+
+  mlir::Value getAndIncrementIndex(mlir::Location loc,
+                                   fir::FirOpBuilder &builder,
+                                   mlir::Value increment) const {
+    mlir::Value indexValue = builder.create<fir::LoadOp>(loc, indexVar);
+    indexValue =
+        builder.create<mlir::arith::AddIOp>(loc, indexValue, increment);
+    builder.create<fir::StoreOp>(loc, indexValue, indexVar);
+    return indexValue;
+  }
+
+private:
+  mlir::Value indexVar;
+};
+using InlinedTempStrategy = InlinedTempStrategyImpl<InMemoryCounter>;
+
+// TODO: add and implement AsElementalStrategy.
+
+// TODO: add and implement RuntimeTempStrategy.
+
+/// Wrapper class that dispatch to the selected array constructor lowering
+/// strategy and does nothing else.
+class ArrayCtorLoweringStrategy {
+public:
+  template <typename A>
+  ArrayCtorLoweringStrategy(A &&impl) : implVariant{std::forward<A>(impl)} {}
+
+  void pushValue(mlir::Location loc, fir::FirOpBuilder &builder,
+                 hlfir::Entity value) {
+    return std::visit(
+        [&](auto &impl) { return impl.pushValue(loc, builder, value); },
+        implVariant);
+  }
+
+  mlir::Value startImpliedDo(mlir::Location loc, fir::FirOpBuilder &builder,
+                             mlir::Value lower, mlir::Value upper,
+                             mlir::Value stride) {
+    return std::visit(
+        [&](auto &impl) {
+          return impl.startImpliedDo(loc, builder, lower, upper, stride);
+        },
+        implVariant);
+  }
+
+  hlfir::Entity finishArrayCtorLowering(mlir::Location loc,
+                                        fir::FirOpBuilder &builder) {
+    return std::visit(
+        [&](auto &impl) { return impl.finishArrayCtorLowering(loc, builder); },
+        implVariant);
+  }
+
+private:
+  std::variant<InlinedTempStrategy, LooplessInlinedTempStrategy> implVariant;
+};
+} // namespace
+
+//===----------------------------------------------------------------------===//
+//   Definition of selectArrayCtorLoweringStrategy and its helpers.
+//   This is the code that analyses the evaluate::ArrayConstructor<T>,
+//   pre-lowers the array constructor extent and length parameters if it can,
+//   and chooses the lowering strategy.
+//===----------------------------------------------------------------------===//
+
+namespace {
+/// Helper class to lower the array constructor type and its length parameters.
+/// The length parameters, if any, are only lowered if this does not require
+/// evaluating an ac-value.
+template <typename T>
+struct LengthAndTypeCollector {
+  static mlir::Type collect(mlir::Location,
+                            Fortran::lower::AbstractConverter &converter,
+                            const Fortran::evaluate::ArrayConstructor<T> &,
+                            Fortran::lower::SymMap &,
+                            Fortran::lower::StatementContext &,
+                            mlir::SmallVectorImpl<mlir::Value> &) {
+    // Numerical and Logical types.
+    return Fortran::lower::getFIRType(&converter.getMLIRContext(), T::category,
+                                      T::kind, /*lenParams*/ {});
+  }
+};
+
+template <>
+struct LengthAndTypeCollector<Fortran::evaluate::SomeDerived> {
+  static mlir::Type collect(
+      mlir::Location loc, Fortran::lower::AbstractConverter &converter,
+      const Fortran::evaluate::ArrayConstructor<Fortran::evaluate::SomeDerived>
+          &arrayCtorExpr,
+      Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
+      mlir::SmallVectorImpl<mlir::Value> &lengths) {
+    TODO(loc, "collect derived type and length");
+  }
+};
+
+template <int Kind>
+using Character =
+    Fortran::evaluate::Type<Fortran::common::TypeCategory::Character, Kind>;
+template <int Kind>
+struct LengthAndTypeCollector<Character<Kind>> {
+  static mlir::Type collect(
+      mlir::Location loc, Fortran::lower::AbstractConverter &converter,
+      const Fortran::evaluate::ArrayConstructor<Character<Kind>> &arrayCtorExpr,
+      Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
+      mlir::SmallVectorImpl<mlir::Value> &lengths) {
+    TODO(loc, "collect character type and length");
+  }
+};
+} // namespace
+
+/// Does the array constructor have length parameters that
+/// LengthAndTypeCollector::collect could not lower because this requires
+/// lowering an ac-value and must be delayed?
+static bool
+failedToGatherLengthParameters(mlir::Type elementType,
+                               llvm::ArrayRef<mlir::Value> lengths) {
+  return (elementType.isa<fir::CharacterType>() ||
+          fir::isRecordWithTypeParameters(elementType)) &&
+         lengths.empty();
+}
+
+namespace {
+/// Structure that analyses the ac-value and implied-do of
+/// evaluate::ArrayConstructor before they are lowered. It does not generate any
+/// IR. The result of this analysis pass is used to select the lowering
+/// strategy.
+struct ArrayCtorAnalysis {
+  template <typename T>
+  ArrayCtorAnalysis(
+      const Fortran::evaluate::ArrayConstructor<T> &arrayCtorExpr);
+
+  // Can the array constructor easily be rewritten into an hlfir.elemental ?
+  bool isSingleImpliedDoWithOneScalarExpr() const {
+    return !anyArrayExpr && isPerfectLoopNest &&
+           innerNumberOfExprIfPrefectNest == 1 && depthIfPerfectLoopNest == 1;
+  }
+
+  bool anyImpliedDo{false};
+  bool anyArrayExpr{false};
+  bool isPerfectLoopNest{true};
+  std::int64_t innerNumberOfExprIfPrefectNest = 0;
+  std::int64_t depthIfPerfectLoopNest = 0;
+};
+} // namespace
+
+template <typename T>
+ArrayCtorAnalysis::ArrayCtorAnalysis(
+    const Fortran::evaluate::ArrayConstructor<T> &arrayCtorExpr) {
+  llvm::SmallVector<const Fortran::evaluate::ArrayConstructorValues<T> *>
+      arrayValueListStack{&arrayCtorExpr};
+  // Loop through the ac-value-list(s) of the array constructor.
+  while (!arrayValueListStack.empty()) {
+    std::int64_t localNumberOfImpliedDo = 0;
+    std::int64_t localNumberOfExpr = 0;
+    // Loop though the ac-value of an ac-value list, and add any nested
+    // ac-value-list of ac-implied-do to the stack.
+    for (const Fortran::evaluate::ArrayConstructorValue<T> &acValue :
+         *arrayValueListStack.pop_back_val())
+      std::visit(Fortran::common::visitors{
+                     [&](const Fortran::evaluate::ImpliedDo<T> &impledDo) {
+                       arrayValueListStack.push_back(&impledDo.values());
+                       localNumberOfImpliedDo++;
+                     },
+                     [&](const Fortran::evaluate::Expr<T> &expr) {
+                       localNumberOfExpr++;
+                       anyArrayExpr = anyArrayExpr || expr.Rank() > 0;
+                     }},
+                 acValue.u);
+    anyImpliedDo = anyImpliedDo || localNumberOfImpliedDo > 0;
+
+    if (localNumberOfImpliedDo == 0) {
+      // Leaf ac-value-list in the array constructor ac-value tree.
+      if (isPerfectLoopNest)
+        // This this the only leaf of the array-constructor (the array
+        // constructor is a nest of single implied-do with a list of expression
+        // in the last deeper implied do). e.g: "[((i+j, i=1,n)j=1,m)]".
+        innerNumberOfExprIfPrefectNest = localNumberOfExpr;
+    } else if (localNumberOfImpliedDo == 1 && localNumberOfExpr == 0) {
+      // Perfect implied-do nest new level.
+      ++depthIfPerfectLoopNest;
+    } else {
+      // More than one implied-do, or at least one implied-do and an expr
+      // at that level. This will not form a perfect nest. Examples:
+      // "[a, (i, i=1,n)]" or "[(i, i=1,n), (j, j=1,m)]".
+      isPerfectLoopNest = false;
+    }
+  }
+}
+
+/// Helper to lower a scalar extent expression (like implied-do bounds).
+static mlir::Value lowerExtentExpr(mlir::Location loc,
+                                   Fortran::lower::AbstractConverter &converter,
+                                   Fortran::lower::SymMap &symMap,
+                                   Fortran::lower::StatementContext &stmtCtx,
+                                   const Fortran::evaluate::ExtentExpr &expr) {
+  fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+  mlir::IndexType idxTy = builder.getIndexType();
+  hlfir::Entity value = Fortran::lower::convertExprToHLFIR(
+      loc, converter, toEvExpr(expr), symMap, stmtCtx);
+  value = hlfir::loadTrivialScalar(loc, builder, value);
+  return builder.createConvert(loc, idxTy, value);
+}
+
+/// Does \p expr contain no calls to user function?
+static bool isCallFreeExpr(const Fortran::evaluate::ExtentExpr &expr) {
+  for (const Fortran::semantics::Symbol &symbol :
+       Fortran::evaluate::CollectSymbols(expr))
+    if (Fortran::semantics::IsProcedure(symbol))
+      return false;
+  return true;
+}
+
+/// Core function that pre-lowers the extent and length parameters of
+/// array constructors if it can, runs the ac-value analysis and
+/// select the lowering strategy accordingly.
+template <typename T>
+static ArrayCtorLoweringStrategy selectArrayCtorLoweringStrategy(
+    mlir::Location loc, Fortran::lower::AbstractConverter &converter,
+    const Fortran::evaluate::ArrayConstructor<T> &arrayCtorExpr,
+    Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
+  fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+  mlir::Type idxType = builder.getIndexType();
+  // Try to gather the array constructor extent.
+  mlir::Value extent;
+  fir::SequenceType::Extent typeExtent = fir::SequenceType::getUnknownExtent();
+  auto shapeExpr =
+      Fortran::evaluate::GetShape(converter.getFoldingContext(), arrayCtorExpr);
+  if (shapeExpr && shapeExpr->size() == 1 && (*shapeExpr)[0]) {
+    const Fortran::evaluate::ExtentExpr &extentExpr = *(*shapeExpr)[0];
+    if (auto constantExtent = Fortran::evaluate::ToInt64(extentExpr)) {
+      typeExtent = *constantExtent;
+      extent = builder.createIntegerConstant(loc, idxType, typeExtent);
+    } else if (isCallFreeExpr(extentExpr)) {
+      // The expression built by expression analysis for the array constructor
+      // extent does not contain procedure symbols. It is side effect free.
+      // This could be relaxed to allow pure procedure, but some care must
+      // be taken to not bring in "unmapped" symbols from callee scopes.
+      extent = lowerExtentExpr(loc, converter, symMap, stmtCtx, extentExpr);
+    }
+    // Otherwise, the temporary will have to be built step by step with
+    // reallocation and the extent will only be known at the end of the array
+    // constructor evaluation.
+  }
+  // Convert the array constructor type and try to gather its length parameter
+  // values, if any.
+  mlir::SmallVector<mlir::Value> lengths;
+  mlir::Type elementType = LengthAndTypeCollector<T>::collect(
+      loc, converter, arrayCtorExpr, symMap, stmtCtx, lengths);
+  // Run an analysis of the array constructor ac-value.
+  ArrayCtorAnalysis analysis(arrayCtorExpr);
+  bool needToEvaluateOneExprToGetLengthParameters =
+      failedToGatherLengthParameters(elementType, lengths);
+
+  // Based on what was gathered and the result of the analysis, select and
+  // instantiate the right lowering strategy for the array constructor.
+  if (!extent || needToEvaluateOneExprToGetLengthParameters ||
+      analysis.anyArrayExpr)
+    TODO(loc, "Lowering of array constructor requiring the runtime");
+
+  auto declaredType = fir::SequenceType::get({typeExtent}, elementType);
+  if (analysis.isSingleImpliedDoWithOneScalarExpr())
+    TODO(loc, "Lowering of array constructor as hlfir.elemental");
+
+  if (analysis.anyImpliedDo)
+    return InlinedTempStrategy(loc, builder, declaredType, extent, lengths);
+
+  return LooplessInlinedTempStrategy(loc, builder, declaredType, extent,
+                                     lengths);
+}
+
+/// Lower an ac-value expression \p expr and forward it to the selected
+/// lowering strategy \p arrayBuilder,
+template <typename T>
+static void genAcValue(mlir::Location loc,
+                       Fortran::lower::AbstractConverter &converter,
+                       const Fortran::evaluate::Expr<T> &expr,
+                       Fortran::lower::SymMap &symMap,
+                       Fortran::lower::StatementContext &stmtCtx,
+                       ArrayCtorLoweringStrategy &arrayBuilder) {
+  if (expr.Rank() != 0)
+    TODO(loc, "array constructor with array ac-value in HLFIR");
+  // TODO: get rid of the toEvExpr indirection.
+  fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+  hlfir::Entity value = Fortran::lower::convertExprToHLFIR(
+      loc, converter, toEvExpr(expr), symMap, stmtCtx);
+  value = hlfir::loadTrivialScalar(loc, builder, value);
+  arrayBuilder.pushValue(loc, builder, value);
+}
+
+/// Lowers an ac-value implied-do \p impledDo according to the selected
+/// lowering strategy \p arrayBuilder.
+template <typename T>
+static void genAcValue(mlir::Location loc,
+                       Fortran::lower::AbstractConverter &converter,
+                       const Fortran::evaluate::ImpliedDo<T> &impledDo,
+                       Fortran::lower::SymMap &symMap,
+                       Fortran::lower::StatementContext &stmtCtx,
+                       ArrayCtorLoweringStrategy &arrayBuilder) {
+  auto lowerIndex =
+      [&](const Fortran::evaluate::ExtentExpr expr) -> mlir::Value {
+    return lowerExtentExpr(loc, converter, symMap, stmtCtx, expr);
+  };
+  mlir::Value lower = lowerIndex(impledDo.lower());
+  mlir::Value upper = lowerIndex(impledDo.upper());
+  mlir::Value stride = lowerIndex(impledDo.stride());
+  fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+  mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint();
+  mlir::Value impliedDoIndexValue =
+      arrayBuilder.startImpliedDo(loc, builder, lower, upper, stride);
+  symMap.pushImpliedDoBinding(toStringRef(impledDo.name()),
+                              impliedDoIndexValue);
+  stmtCtx.pushScope();
+
+  for (const auto &acValue : impledDo.values())
+    std::visit(
+        [&](const auto &x) {
+          genAcValue(loc, converter, x, symMap, stmtCtx, arrayBuilder);
+        },
+        acValue.u);
+
+  stmtCtx.finalizeAndPop();
+  symMap.popImpliedDoBinding();
+  builder.restoreInsertionPoint(insertPt);
+}
+
+/// Entry point for evaluate::ArrayConstructor lowering.
+template <typename T>
+hlfir::EntityWithAttributes Fortran::lower::ArrayConstructorBuilder<T>::gen(
+    mlir::Location loc, Fortran::lower::AbstractConverter &converter,
+    const Fortran::evaluate::ArrayConstructor<T> &arrayCtorExpr,
+    Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
+  fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+  // Select the lowering strategy given the array constructor.
+  auto arrayBuilder = selectArrayCtorLoweringStrategy(
+      loc, converter, arrayCtorExpr, symMap, stmtCtx);
+  // Run the array lowering strategy through the ac-values.
+  for (const auto &acValue : arrayCtorExpr)
+    std::visit(
+        [&](const auto &x) {
+          genAcValue(loc, converter, x, symMap, stmtCtx, arrayBuilder);
+        },
+        acValue.u);
+  hlfir::Entity hlfirExpr = arrayBuilder.finishArrayCtorLowering(loc, builder);
+  // Insert the clean-up for the created hlfir.expr.
+  fir::FirOpBuilder *bldr = &builder;
+  stmtCtx.attachCleanup(
+      [=]() { bldr->create<hlfir::DestroyOp>(loc, hlfirExpr); });
+  return hlfir::EntityWithAttributes{hlfirExpr};
+}
+
+using namespace Fortran::evaluate;
+using namespace Fortran::common;
+FOR_EACH_SPECIFIC_TYPE(template class Fortran::lower::ArrayConstructorBuilder, )

diff  --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp
index f0344ba59e0bd..49a2c52629335 100644
--- a/flang/lib/Lower/ConvertExprToHLFIR.cpp
+++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp
@@ -14,6 +14,7 @@
 #include "flang/Evaluate/shape.h"
 #include "flang/Lower/AbstractConverter.h"
 #include "flang/Lower/CallInterface.h"
+#include "flang/Lower/ConvertArrayConstructor.h"
 #include "flang/Lower/ConvertCall.h"
 #include "flang/Lower/ConvertConstant.h"
 #include "flang/Lower/ConvertProcedureDesignator.h"
@@ -1074,8 +1075,9 @@ class HlfirBuilder {
 
   template <typename T>
   hlfir::EntityWithAttributes
-  gen(const Fortran::evaluate::ArrayConstructor<T> &expr) {
-    TODO(getLoc(), "lowering ArrayCtor to HLFIR");
+  gen(const Fortran::evaluate::ArrayConstructor<T> &arrayCtor) {
+    return Fortran::lower::ArrayConstructorBuilder<T>::gen(
+        getLoc(), getConverter(), arrayCtor, getSymMap(), getStmtCtx());
   }
 
   template <typename D, typename R, typename O>
@@ -1208,7 +1210,9 @@ class HlfirBuilder {
 
   hlfir::EntityWithAttributes
   gen(const Fortran::evaluate::ImpliedDoIndex &var) {
-    TODO(getLoc(), "lowering implied do index to HLFIR");
+    mlir::Value value = symMap.lookupImpliedDo(toStringRef(var.name));
+    assert(value && "impled do was not mapped");
+    return hlfir::EntityWithAttributes{value};
   }
 
   hlfir::EntityWithAttributes

diff  --git a/flang/test/Lower/HLFIR/array-ctor-as-inlined-temp.f90 b/flang/test/Lower/HLFIR/array-ctor-as-inlined-temp.f90
new file mode 100644
index 0000000000000..a3e0d09cfb71a
--- /dev/null
+++ b/flang/test/Lower/HLFIR/array-ctor-as-inlined-temp.f90
@@ -0,0 +1,280 @@
+! Test lowering of array constructors as inlined temporary.
+! RUN: bbc -emit-fir -hlfir -o - %s | FileCheck %s
+
+subroutine test_simple(i)
+  call takes_int([42, i])
+end subroutine
+! CHECK-LABEL: func.func @_QPtest_simple(
+! CHECK:  %[[VAL_1:.*]]:2 = hlfir.declare {{.*}}Ei
+! CHECK:  %[[VAL_2:.*]] = arith.constant 2 : index
+! CHECK:  %[[VAL_3:.*]] = arith.constant 1 : index
+! CHECK:  %[[VAL_4:.*]] = fir.allocmem !fir.array<2xi32> {bindc_name = ".tmp.arrayctor", uniq_name = ""}
+! CHECK:  %[[VAL_5:.*]] = fir.shape %[[VAL_2]] : (index) -> !fir.shape<1>
+! CHECK:  %[[VAL_6:.*]]:2 = hlfir.declare %[[VAL_4]](%[[VAL_5]]) {uniq_name = ".tmp.arrayctor"} : (!fir.heap<!fir.array<2xi32>>, !fir.shape<1>) -> (!fir.heap<!fir.array<2xi32>>, !fir.heap<!fir.array<2xi32>>)
+! CHECK:  %[[VAL_7:.*]] = arith.constant 42 : i32
+! CHECK:  %[[VAL_8:.*]] = arith.addi %[[VAL_3]], %[[VAL_3]] : index
+! CHECK:  %[[VAL_9:.*]] = hlfir.designate %[[VAL_6]]#0 (%[[VAL_3]])  : (!fir.heap<!fir.array<2xi32>>, index) -> !fir.ref<i32>
+! CHECK:  hlfir.assign %[[VAL_7]] to %[[VAL_9]] : i32, !fir.ref<i32>
+! CHECK:  %[[VAL_10:.*]] = fir.load %[[VAL_1]]#0 : !fir.ref<i32>
+! CHECK:  %[[VAL_11:.*]] = hlfir.designate %[[VAL_6]]#0 (%[[VAL_8]])  : (!fir.heap<!fir.array<2xi32>>, index) -> !fir.ref<i32>
+! CHECK:  hlfir.assign %[[VAL_10]] to %[[VAL_11]] : i32, !fir.ref<i32>
+! CHECK:  %[[VAL_12:.*]] = arith.constant true
+! CHECK:  %[[VAL_13:.*]] = hlfir.as_expr %[[VAL_6]]#0 move %[[VAL_12]] : (!fir.heap<!fir.array<2xi32>>, i1) -> !hlfir.expr<2xi32>
+! CHECK:  fir.call
+! CHECK:  hlfir.destroy %[[VAL_13]] : !hlfir.expr<2xi32>
+
+subroutine test_simple_real(x)
+  real(2) :: x
+  call takes_real_2([x, 0._2])
+end subroutine
+! CHECK-LABEL: func.func @_QPtest_simple_real(
+! CHECK:  %[[VAL_1:.*]]:2 = hlfir.declare {{.*}}Ex
+! CHECK:  %[[VAL_2:.*]] = arith.constant 2 : index
+! CHECK:  %[[VAL_3:.*]] = arith.constant 1 : index
+! CHECK:  %[[VAL_4:.*]] = fir.allocmem !fir.array<2xf16> {bindc_name = ".tmp.arrayctor", uniq_name = ""}
+! CHECK:  %[[VAL_5:.*]] = fir.shape %[[VAL_2]] : (index) -> !fir.shape<1>
+! CHECK:  %[[VAL_6:.*]]:2 = hlfir.declare %[[VAL_4]](%[[VAL_5]]) {uniq_name = ".tmp.arrayctor"} : (!fir.heap<!fir.array<2xf16>>, !fir.shape<1>) -> (!fir.heap<!fir.array<2xf16>>, !fir.heap<!fir.array<2xf16>>)
+! CHECK:  %[[VAL_7:.*]] = fir.load %[[VAL_1]]#0 : !fir.ref<f16>
+! CHECK:  %[[VAL_8:.*]] = arith.addi %[[VAL_3]], %[[VAL_3]] : index
+! CHECK:  %[[VAL_9:.*]] = hlfir.designate %[[VAL_6]]#0 (%[[VAL_3]])  : (!fir.heap<!fir.array<2xf16>>, index) -> !fir.ref<f16>
+! CHECK:  hlfir.assign %[[VAL_7]] to %[[VAL_9]] : f16, !fir.ref<f16>
+! CHECK:  %[[VAL_10:.*]] = arith.constant 0.000000e+00 : f16
+! CHECK:  %[[VAL_11:.*]] = hlfir.designate %[[VAL_6]]#0 (%[[VAL_8]])  : (!fir.heap<!fir.array<2xf16>>, index) -> !fir.ref<f16>
+! CHECK:  hlfir.assign %[[VAL_10]] to %[[VAL_11]] : f16, !fir.ref<f16>
+! CHECK:  %[[VAL_12:.*]] = arith.constant true
+! CHECK:  %[[VAL_13:.*]] = hlfir.as_expr %[[VAL_6]]#0 move %[[VAL_12]] : (!fir.heap<!fir.array<2xf16>>, i1) -> !hlfir.expr<2xf16>
+! CHECK:  fir.call
+! CHECK:  hlfir.destroy %[[VAL_13]] : !hlfir.expr<2xf16>
+
+subroutine test_simple_complex(z)
+  complex :: z
+  call takes_cmplx_8([complex(8):: 42, z])
+end subroutine
+! CHECK-LABEL: func.func @_QPtest_simple_complex(
+! CHECK:  %[[VAL_1:.*]]:2 = hlfir.declare {{.*}}Ez
+! CHECK:  %[[VAL_2:.*]] = arith.constant 2 : index
+! CHECK:  %[[VAL_3:.*]] = arith.constant 1 : index
+! CHECK:  %[[VAL_4:.*]] = fir.allocmem !fir.array<2x!fir.complex<8>> {bindc_name = ".tmp.arrayctor", uniq_name = ""}
+! CHECK:  %[[VAL_5:.*]] = fir.shape %[[VAL_2]] : (index) -> !fir.shape<1>
+! CHECK:  %[[VAL_6:.*]]:2 = hlfir.declare %[[VAL_4]](%[[VAL_5]]) {uniq_name = ".tmp.arrayctor"} : (!fir.heap<!fir.array<2x!fir.complex<8>>>, !fir.shape<1>) -> (!fir.heap<!fir.array<2x!fir.complex<8>>>, !fir.heap<!fir.array<2x!fir.complex<8>>>)
+! CHECK:  %[[VAL_7:.*]] = arith.constant 42 : i32
+! CHECK:  %[[VAL_8:.*]] = fir.convert %[[VAL_7]] : (i32) -> f64
+! CHECK:  %[[VAL_9:.*]] = arith.constant 0.000000e+00 : f64
+! CHECK:  %[[VAL_10:.*]] = fir.undefined !fir.complex<8>
+! CHECK:  %[[VAL_11:.*]] = fir.insert_value %[[VAL_10]], %[[VAL_8]], [0 : index] : (!fir.complex<8>, f64) -> !fir.complex<8>
+! CHECK:  %[[VAL_12:.*]] = fir.insert_value %[[VAL_11]], %[[VAL_9]], [1 : index] : (!fir.complex<8>, f64) -> !fir.complex<8>
+! CHECK:  %[[VAL_13:.*]] = arith.addi %[[VAL_3]], %[[VAL_3]] : index
+! CHECK:  %[[VAL_14:.*]] = hlfir.designate %[[VAL_6]]#0 (%[[VAL_3]])  : (!fir.heap<!fir.array<2x!fir.complex<8>>>, index) -> !fir.ref<!fir.complex<8>>
+! CHECK:  hlfir.assign %[[VAL_12]] to %[[VAL_14]] : !fir.complex<8>, !fir.ref<!fir.complex<8>>
+! CHECK:  %[[VAL_15:.*]] = fir.load %[[VAL_1]]#0 : !fir.ref<!fir.complex<4>>
+! CHECK:  %[[VAL_16:.*]] = fir.convert %[[VAL_15]] : (!fir.complex<4>) -> !fir.complex<8>
+! CHECK:  %[[VAL_17:.*]] = hlfir.designate %[[VAL_6]]#0 (%[[VAL_13]])  : (!fir.heap<!fir.array<2x!fir.complex<8>>>, index) -> !fir.ref<!fir.complex<8>>
+! CHECK:  hlfir.assign %[[VAL_16]] to %[[VAL_17]] : !fir.complex<8>, !fir.ref<!fir.complex<8>>
+! CHECK:  %[[VAL_18:.*]] = arith.constant true
+! CHECK:  %[[VAL_19:.*]] = hlfir.as_expr %[[VAL_6]]#0 move %[[VAL_18]] : (!fir.heap<!fir.array<2x!fir.complex<8>>>, i1) -> !hlfir.expr<2x!fir.complex<8>>
+! CHECK:  fir.call
+! CHECK:  hlfir.destroy %[[VAL_19]] : !hlfir.expr<2x!fir.complex<8>>
+
+subroutine test_simple_logical(a, b)
+  logical :: a, b
+  call takes_logical([a, a.and.b])
+end subroutine
+! CHECK-LABEL: func.func @_QPtest_simple_logical(
+! CHECK:  %[[VAL_2:.*]]:2 = hlfir.declare {{.*}}Ea
+! CHECK:  %[[VAL_3:.*]]:2 = hlfir.declare {{.*}}Eb
+! CHECK:  %[[VAL_4:.*]] = arith.constant 2 : index
+! CHECK:  %[[VAL_5:.*]] = arith.constant 1 : index
+! CHECK:  %[[VAL_6:.*]] = fir.allocmem !fir.array<2x!fir.logical<4>> {bindc_name = ".tmp.arrayctor", uniq_name = ""}
+! CHECK:  %[[VAL_7:.*]] = fir.shape %[[VAL_4]] : (index) -> !fir.shape<1>
+! CHECK:  %[[VAL_8:.*]]:2 = hlfir.declare %[[VAL_6]](%[[VAL_7]]) {uniq_name = ".tmp.arrayctor"} : (!fir.heap<!fir.array<2x!fir.logical<4>>>, !fir.shape<1>) -> (!fir.heap<!fir.array<2x!fir.logical<4>>>, !fir.heap<!fir.array<2x!fir.logical<4>>>)
+! CHECK:  %[[VAL_9:.*]] = fir.load %[[VAL_2]]#0 : !fir.ref<!fir.logical<4>>
+! CHECK:  %[[VAL_10:.*]] = arith.addi %[[VAL_5]], %[[VAL_5]] : index
+! CHECK:  %[[VAL_11:.*]] = hlfir.designate %[[VAL_8]]#0 (%[[VAL_5]])  : (!fir.heap<!fir.array<2x!fir.logical<4>>>, index) -> !fir.ref<!fir.logical<4>>
+! CHECK:  hlfir.assign %[[VAL_9]] to %[[VAL_11]] : !fir.logical<4>, !fir.ref<!fir.logical<4>>
+! CHECK:  %[[VAL_12:.*]] = fir.load %[[VAL_2]]#0 : !fir.ref<!fir.logical<4>>
+! CHECK:  %[[VAL_13:.*]] = fir.load %[[VAL_3]]#0 : !fir.ref<!fir.logical<4>>
+! CHECK:  %[[VAL_14:.*]] = fir.convert %[[VAL_12]] : (!fir.logical<4>) -> i1
+! CHECK:  %[[VAL_15:.*]] = fir.convert %[[VAL_13]] : (!fir.logical<4>) -> i1
+! CHECK:  %[[VAL_16:.*]] = arith.andi %[[VAL_14]], %[[VAL_15]] : i1
+! CHECK:  %[[VAL_17:.*]] = hlfir.designate %[[VAL_8]]#0 (%[[VAL_10]])  : (!fir.heap<!fir.array<2x!fir.logical<4>>>, index) -> !fir.ref<!fir.logical<4>>
+! CHECK:  hlfir.assign %[[VAL_16]] to %[[VAL_17]] : i1, !fir.ref<!fir.logical<4>>
+! CHECK:  %[[VAL_18:.*]] = arith.constant true
+! CHECK:  %[[VAL_19:.*]] = hlfir.as_expr %[[VAL_8]]#0 move %[[VAL_18]] : (!fir.heap<!fir.array<2x!fir.logical<4>>>, i1) -> !hlfir.expr<2x!fir.logical<4>>
+! CHECK:  fir.call
+! CHECK:  hlfir.destroy %[[VAL_19]] : !hlfir.expr<2x!fir.logical<4>>
+
+subroutine test_implied_do(n)
+  integer(8) :: n
+  ! This implied do cannot easily be promoted to hlfir.elemental because
+  ! the implied do contains more than one scalar ac-value.
+  call takes_int([(42, j, j=1,n)])
+end subroutine
+! CHECK-LABEL: func.func @_QPtest_implied_do(
+! CHECK:  %[[VAL_1:.*]] = fir.alloca index
+! CHECK:  %[[VAL_2:.*]]:2 = hlfir.declare {{.*}}En
+! CHECK:  %[[VAL_3:.*]] = arith.constant 0 : i64
+! CHECK:  %[[VAL_4:.*]] = arith.constant 2 : i64
+! CHECK:  %[[VAL_5:.*]] = fir.load %[[VAL_2]]#0 : !fir.ref<i64>
+! CHECK:  %[[VAL_6:.*]] = arith.constant 1 : i64
+! CHECK:  %[[VAL_7:.*]] = arith.subi %[[VAL_5]], %[[VAL_6]] : i64
+! CHECK:  %[[VAL_8:.*]] = arith.constant 1 : i64
+! CHECK:  %[[VAL_9:.*]] = arith.addi %[[VAL_7]], %[[VAL_8]] : i64
+! CHECK:  %[[VAL_10:.*]] = arith.constant 1 : i64
+! CHECK:  %[[VAL_11:.*]] = arith.divsi %[[VAL_9]], %[[VAL_10]] : i64
+! CHECK:  %[[VAL_12:.*]] = arith.constant 0 : i64
+! CHECK:  %[[VAL_13:.*]] = arith.cmpi sgt, %[[VAL_11]], %[[VAL_12]] : i64
+! CHECK:  %[[VAL_14:.*]] = arith.select %[[VAL_13]], %[[VAL_11]], %[[VAL_12]] : i64
+! CHECK:  %[[VAL_15:.*]] = arith.muli %[[VAL_4]], %[[VAL_14]] : i64
+! CHECK:  %[[VAL_16:.*]] = arith.addi %[[VAL_3]], %[[VAL_15]] : i64
+! CHECK:  %[[VAL_17:.*]] = fir.convert %[[VAL_16]] : (i64) -> index
+! CHECK:  %[[VAL_18:.*]] = arith.constant 1 : index
+! CHECK:  fir.store %[[VAL_18]] to %[[VAL_1]] : !fir.ref<index>
+! CHECK:  %[[VAL_19:.*]] = fir.allocmem !fir.array<?xi32>, %[[VAL_17]] {bindc_name = ".tmp.arrayctor", uniq_name = ""}
+! CHECK:  %[[VAL_20:.*]] = fir.shape %[[VAL_17]] : (index) -> !fir.shape<1>
+! CHECK:  %[[VAL_21:.*]]:2 = hlfir.declare %[[VAL_19]](%[[VAL_20]]) {uniq_name = ".tmp.arrayctor"} : (!fir.heap<!fir.array<?xi32>>, !fir.shape<1>) -> (!fir.box<!fir.array<?xi32>>, !fir.heap<!fir.array<?xi32>>)
+! CHECK:  %[[VAL_22:.*]] = arith.constant 1 : i64
+! CHECK:  %[[VAL_23:.*]] = fir.convert %[[VAL_22]] : (i64) -> index
+! CHECK:  %[[VAL_24:.*]] = fir.load %[[VAL_2]]#0 : !fir.ref<i64>
+! CHECK:  %[[VAL_25:.*]] = fir.convert %[[VAL_24]] : (i64) -> index
+! CHECK:  %[[VAL_26:.*]] = arith.constant 1 : i64
+! CHECK:  %[[VAL_27:.*]] = fir.convert %[[VAL_26]] : (i64) -> index
+! CHECK:  fir.do_loop %[[VAL_28:.*]] = %[[VAL_23]] to %[[VAL_25]] step %[[VAL_27]] {
+! CHECK:    %[[VAL_29:.*]] = arith.constant 42 : i32
+! CHECK:    %[[VAL_30:.*]] = fir.load %[[VAL_1]] : !fir.ref<index>
+! CHECK:    %[[VAL_31:.*]] = arith.addi %[[VAL_30]], %[[VAL_18]] : index
+! CHECK:    fir.store %[[VAL_31]] to %[[VAL_1]] : !fir.ref<index>
+! CHECK:    %[[VAL_32:.*]] = hlfir.designate %[[VAL_21]]#0 (%[[VAL_31]])  : (!fir.box<!fir.array<?xi32>>, index) -> !fir.ref<i32>
+! CHECK:    hlfir.assign %[[VAL_29]] to %[[VAL_32]] : i32, !fir.ref<i32>
+! CHECK:    %[[VAL_33:.*]] = fir.convert %[[VAL_28]] : (index) -> i32
+! CHECK:    %[[VAL_34:.*]] = fir.load %[[VAL_1]] : !fir.ref<index>
+! CHECK:    %[[VAL_35:.*]] = arith.addi %[[VAL_34]], %[[VAL_18]] : index
+! CHECK:    fir.store %[[VAL_35]] to %[[VAL_1]] : !fir.ref<index>
+! CHECK:    %[[VAL_36:.*]] = hlfir.designate %[[VAL_21]]#0 (%[[VAL_35]])  : (!fir.box<!fir.array<?xi32>>, index) -> !fir.ref<i32>
+! CHECK:    hlfir.assign %[[VAL_33]] to %[[VAL_36]] : i32, !fir.ref<i32>
+! CHECK:  }
+! CHECK:  %[[VAL_37:.*]] = arith.constant true
+! CHECK:  %[[VAL_38:.*]] = hlfir.as_expr %[[VAL_21]]#0 move %[[VAL_37]] : (!fir.box<!fir.array<?xi32>>, i1) -> !hlfir.expr<?xi32>
+! CHECK:  fir.call
+! CHECK:  hlfir.destroy %[[VAL_38]] : !hlfir.expr<?xi32>
+
+subroutine test_strided_implied_do(lb, ub, stride)
+  integer(8) :: lb, ub, stride
+  call takes_int([(42, j, j=lb,ub,stride)])
+end subroutine
+! CHECK-LABEL: func.func @_QPtest_strided_implied_do(
+! CHECK:  %[[VAL_3:.*]] = fir.alloca index
+! CHECK:  %[[VAL_4:.*]]:2 = hlfir.declare {{.*}}Elb
+! CHECK:  %[[VAL_5:.*]]:2 = hlfir.declare {{.*}}Estride
+! CHECK:  %[[VAL_6:.*]]:2 = hlfir.declare {{.*}}Eub
+! CHECK:  %[[VAL_7:.*]] = arith.constant 0 : i64
+! CHECK:  %[[VAL_8:.*]] = arith.constant 2 : i64
+! CHECK:  %[[VAL_9:.*]] = fir.load %[[VAL_6]]#0 : !fir.ref<i64>
+! CHECK:  %[[VAL_10:.*]] = fir.load %[[VAL_4]]#0 : !fir.ref<i64>
+! CHECK:  %[[VAL_11:.*]] = arith.subi %[[VAL_9]], %[[VAL_10]] : i64
+! CHECK:  %[[VAL_12:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref<i64>
+! CHECK:  %[[VAL_13:.*]] = arith.addi %[[VAL_11]], %[[VAL_12]] : i64
+! CHECK:  %[[VAL_14:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref<i64>
+! CHECK:  %[[VAL_15:.*]] = arith.divsi %[[VAL_13]], %[[VAL_14]] : i64
+! CHECK:  %[[VAL_16:.*]] = arith.constant 0 : i64
+! CHECK:  %[[VAL_17:.*]] = arith.cmpi sgt, %[[VAL_15]], %[[VAL_16]] : i64
+! CHECK:  %[[VAL_18:.*]] = arith.select %[[VAL_17]], %[[VAL_15]], %[[VAL_16]] : i64
+! CHECK:  %[[VAL_19:.*]] = arith.muli %[[VAL_8]], %[[VAL_18]] : i64
+! CHECK:  %[[VAL_20:.*]] = arith.addi %[[VAL_7]], %[[VAL_19]] : i64
+! CHECK:  %[[VAL_21:.*]] = fir.convert %[[VAL_20]] : (i64) -> index
+! CHECK:  %[[VAL_22:.*]] = arith.constant 1 : index
+! CHECK:  fir.store %[[VAL_22]] to %[[VAL_3]] : !fir.ref<index>
+! CHECK:  %[[VAL_23:.*]] = fir.allocmem !fir.array<?xi32>, %[[VAL_21]] {bindc_name = ".tmp.arrayctor", uniq_name = ""}
+! CHECK:  %[[VAL_24:.*]] = fir.shape %[[VAL_21]] : (index) -> !fir.shape<1>
+! CHECK:  %[[VAL_25:.*]]:2 = hlfir.declare %[[VAL_23]](%[[VAL_24]]) {uniq_name = ".tmp.arrayctor"} : (!fir.heap<!fir.array<?xi32>>, !fir.shape<1>) -> (!fir.box<!fir.array<?xi32>>, !fir.heap<!fir.array<?xi32>>)
+! CHECK:  %[[VAL_26:.*]] = fir.load %[[VAL_4]]#0 : !fir.ref<i64>
+! CHECK:  %[[VAL_27:.*]] = fir.convert %[[VAL_26]] : (i64) -> index
+! CHECK:  %[[VAL_28:.*]] = fir.load %[[VAL_6]]#0 : !fir.ref<i64>
+! CHECK:  %[[VAL_29:.*]] = fir.convert %[[VAL_28]] : (i64) -> index
+! CHECK:  %[[VAL_30:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref<i64>
+! CHECK:  %[[VAL_31:.*]] = fir.convert %[[VAL_30]] : (i64) -> index
+! CHECK:  fir.do_loop %[[VAL_32:.*]] = %[[VAL_27]] to %[[VAL_29]] step %[[VAL_31]] {
+! CHECK:    %[[VAL_33:.*]] = arith.constant 42 : i32
+! CHECK:    %[[VAL_34:.*]] = fir.load %[[VAL_3]] : !fir.ref<index>
+! CHECK:    %[[VAL_35:.*]] = arith.addi %[[VAL_34]], %[[VAL_22]] : index
+! CHECK:    fir.store %[[VAL_35]] to %[[VAL_3]] : !fir.ref<index>
+! CHECK:    %[[VAL_36:.*]] = hlfir.designate %[[VAL_25]]#0 (%[[VAL_35]])  : (!fir.box<!fir.array<?xi32>>, index) -> !fir.ref<i32>
+! CHECK:    hlfir.assign %[[VAL_33]] to %[[VAL_36]] : i32, !fir.ref<i32>
+! CHECK:    %[[VAL_37:.*]] = fir.convert %[[VAL_32]] : (index) -> i32
+! CHECK:    %[[VAL_38:.*]] = fir.load %[[VAL_3]] : !fir.ref<index>
+! CHECK:    %[[VAL_39:.*]] = arith.addi %[[VAL_38]], %[[VAL_22]] : index
+! CHECK:    fir.store %[[VAL_39]] to %[[VAL_3]] : !fir.ref<index>
+! CHECK:    %[[VAL_40:.*]] = hlfir.designate %[[VAL_25]]#0 (%[[VAL_39]])  : (!fir.box<!fir.array<?xi32>>, index) -> !fir.ref<i32>
+! CHECK:    hlfir.assign %[[VAL_37]] to %[[VAL_40]] : i32, !fir.ref<i32>
+! CHECK:  }
+! CHECK:  %[[VAL_41:.*]] = arith.constant true
+! CHECK:  %[[VAL_42:.*]] = hlfir.as_expr %[[VAL_25]]#0 move %[[VAL_41]] : (!fir.box<!fir.array<?xi32>>, i1) -> !hlfir.expr<?xi32>
+! CHECK:  fir.call
+! CHECK:  hlfir.destroy %[[VAL_42]] : !hlfir.expr<?xi32>
+
+subroutine test_nested_implied_do(n, m)
+  integer(8) :: n, m
+  call takes_int([((i+j, i=1,m), j=1,n)])
+end subroutine
+! CHECK-LABEL: func.func @_QPtest_nested_implied_do(
+! CHECK:  %[[VAL_2:.*]] = fir.alloca index
+! CHECK:  %[[VAL_3:.*]]:2 = hlfir.declare {{.*}}Em
+! CHECK:  %[[VAL_4:.*]]:2 = hlfir.declare {{.*}}En
+! CHECK:  %[[VAL_5:.*]] = arith.constant 0 : i64
+! CHECK:  %[[VAL_6:.*]] = arith.constant 0 : i64
+! CHECK:  %[[VAL_7:.*]] = fir.load %[[VAL_3]]#0 : !fir.ref<i64>
+! CHECK:  %[[VAL_8:.*]] = arith.constant 1 : i64
+! CHECK:  %[[VAL_9:.*]] = arith.subi %[[VAL_7]], %[[VAL_8]] : i64
+! CHECK:  %[[VAL_10:.*]] = arith.constant 1 : i64
+! CHECK:  %[[VAL_11:.*]] = arith.addi %[[VAL_9]], %[[VAL_10]] : i64
+! CHECK:  %[[VAL_12:.*]] = arith.constant 1 : i64
+! CHECK:  %[[VAL_13:.*]] = arith.divsi %[[VAL_11]], %[[VAL_12]] : i64
+! CHECK:  %[[VAL_14:.*]] = arith.constant 0 : i64
+! CHECK:  %[[VAL_15:.*]] = arith.cmpi sgt, %[[VAL_13]], %[[VAL_14]] : i64
+! CHECK:  %[[VAL_16:.*]] = arith.select %[[VAL_15]], %[[VAL_13]], %[[VAL_14]] : i64
+! CHECK:  %[[VAL_17:.*]] = arith.addi %[[VAL_6]], %[[VAL_16]] : i64
+! CHECK:  %[[VAL_18:.*]] = fir.load %[[VAL_4]]#0 : !fir.ref<i64>
+! CHECK:  %[[VAL_19:.*]] = arith.constant 1 : i64
+! CHECK:  %[[VAL_20:.*]] = arith.subi %[[VAL_18]], %[[VAL_19]] : i64
+! CHECK:  %[[VAL_21:.*]] = arith.constant 1 : i64
+! CHECK:  %[[VAL_22:.*]] = arith.addi %[[VAL_20]], %[[VAL_21]] : i64
+! CHECK:  %[[VAL_23:.*]] = arith.constant 1 : i64
+! CHECK:  %[[VAL_24:.*]] = arith.divsi %[[VAL_22]], %[[VAL_23]] : i64
+! CHECK:  %[[VAL_25:.*]] = arith.constant 0 : i64
+! CHECK:  %[[VAL_26:.*]] = arith.cmpi sgt, %[[VAL_24]], %[[VAL_25]] : i64
+! CHECK:  %[[VAL_27:.*]] = arith.select %[[VAL_26]], %[[VAL_24]], %[[VAL_25]] : i64
+! CHECK:  %[[VAL_28:.*]] = arith.muli %[[VAL_17]], %[[VAL_27]] : i64
+! CHECK:  %[[VAL_29:.*]] = arith.addi %[[VAL_5]], %[[VAL_28]] : i64
+! CHECK:  %[[VAL_30:.*]] = fir.convert %[[VAL_29]] : (i64) -> index
+! CHECK:  %[[VAL_31:.*]] = arith.constant 1 : index
+! CHECK:  fir.store %[[VAL_31]] to %[[VAL_2]] : !fir.ref<index>
+! CHECK:  %[[VAL_32:.*]] = fir.allocmem !fir.array<?xi32>, %[[VAL_30]] {bindc_name = ".tmp.arrayctor", uniq_name = ""}
+! CHECK:  %[[VAL_33:.*]] = fir.shape %[[VAL_30]] : (index) -> !fir.shape<1>
+! CHECK:  %[[VAL_34:.*]]:2 = hlfir.declare %[[VAL_32]](%[[VAL_33]]) {uniq_name = ".tmp.arrayctor"} : (!fir.heap<!fir.array<?xi32>>, !fir.shape<1>) -> (!fir.box<!fir.array<?xi32>>, !fir.heap<!fir.array<?xi32>>)
+! CHECK:  %[[VAL_35:.*]] = arith.constant 1 : i64
+! CHECK:  %[[VAL_36:.*]] = fir.convert %[[VAL_35]] : (i64) -> index
+! CHECK:  %[[VAL_37:.*]] = fir.load %[[VAL_4]]#0 : !fir.ref<i64>
+! CHECK:  %[[VAL_38:.*]] = fir.convert %[[VAL_37]] : (i64) -> index
+! CHECK:  %[[VAL_39:.*]] = arith.constant 1 : i64
+! CHECK:  %[[VAL_40:.*]] = fir.convert %[[VAL_39]] : (i64) -> index
+! CHECK:  fir.do_loop %[[VAL_41:.*]] = %[[VAL_36]] to %[[VAL_38]] step %[[VAL_40]] {
+! CHECK:    %[[VAL_42:.*]] = arith.constant 1 : i64
+! CHECK:    %[[VAL_43:.*]] = fir.convert %[[VAL_42]] : (i64) -> index
+! CHECK:    %[[VAL_44:.*]] = fir.load %[[VAL_3]]#0 : !fir.ref<i64>
+! CHECK:    %[[VAL_45:.*]] = fir.convert %[[VAL_44]] : (i64) -> index
+! CHECK:    %[[VAL_46:.*]] = arith.constant 1 : i64
+! CHECK:    %[[VAL_47:.*]] = fir.convert %[[VAL_46]] : (i64) -> index
+! CHECK:    fir.do_loop %[[VAL_48:.*]] = %[[VAL_43]] to %[[VAL_45]] step %[[VAL_47]] {
+! CHECK:      %[[VAL_49:.*]] = fir.convert %[[VAL_48]] : (index) -> i32
+! CHECK:      %[[VAL_50:.*]] = fir.convert %[[VAL_41]] : (index) -> i32
+! CHECK:      %[[VAL_51:.*]] = arith.addi %[[VAL_49]], %[[VAL_50]] : i32
+! CHECK:      %[[VAL_52:.*]] = fir.load %[[VAL_2]] : !fir.ref<index>
+! CHECK:      %[[VAL_53:.*]] = arith.addi %[[VAL_52]], %[[VAL_31]] : index
+! CHECK:      fir.store %[[VAL_53]] to %[[VAL_2]] : !fir.ref<index>
+! CHECK:      %[[VAL_54:.*]] = hlfir.designate %[[VAL_34]]#0 (%[[VAL_53]])  : (!fir.box<!fir.array<?xi32>>, index) -> !fir.ref<i32>
+! CHECK:      hlfir.assign %[[VAL_51]] to %[[VAL_54]] : i32, !fir.ref<i32>
+! CHECK:    }
+! CHECK:  }
+! CHECK:  %[[VAL_55:.*]] = arith.constant true
+! CHECK:  %[[VAL_56:.*]] = hlfir.as_expr %[[VAL_34]]#0 move %[[VAL_55]] : (!fir.box<!fir.array<?xi32>>, i1) -> !hlfir.expr<?xi32>
+! CHECK:  fir.call
+! CHECK:  hlfir.destroy %[[VAL_56]] : !hlfir.expr<?xi32>


        


More information about the flang-commits mailing list