[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