#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/Runtime/ArrayConstructor.h"
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
#include "flang/Optimizer/Builder/TemporaryStorage.h"
#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Optimizer/HLFIR/HLFIROps.h"
namespace {
class StrategyBase {
public:
StrategyBase(Fortran::lower::StatementContext &stmtCtx,
Fortran::lower::SymMap &symMap)
: stmtCtx{stmtCtx}, symMap{symMap} {};
virtual ~StrategyBase() = default;
virtual void startImpliedDoScope(llvm::StringRef doName,
mlir::Value indexValue) {
symMap.pushImpliedDoBinding(doName, indexValue);
stmtCtx.pushScope();
}
virtual void endImpliedDoScope() {
stmtCtx.finalizeAndPop();
symMap.popImpliedDoBinding();
}
protected:
Fortran::lower::StatementContext &stmtCtx;
Fortran::lower::SymMap &symMap;
};
template <bool hasLoops>
class InlinedTempStrategyImpl : public StrategyBase,
public fir::factory::HomogeneousScalarStack {
static constexpr char tempName[] = ".tmp.arrayctor";
public:
InlinedTempStrategyImpl(mlir::Location loc, fir::FirOpBuilder &builder,
Fortran::lower::StatementContext &stmtCtx,
Fortran::lower::SymMap &symMap,
fir::SequenceType declaredType, mlir::Value extent,
llvm::ArrayRef<mlir::Value> lengths)
: StrategyBase{stmtCtx, symMap},
fir::factory::HomogeneousScalarStack{
loc, builder, declaredType,
extent, lengths, true,
hasLoops, tempName} {}
using fir::factory::HomogeneousScalarStack::pushValue;
mlir::Value startImpliedDo(mlir::Location loc, fir::FirOpBuilder &builder,
mlir::Value lower, mlir::Value upper,
mlir::Value stride) {
if constexpr (!hasLoops)
fir::emitFatalError(loc, "array constructor lowering is inconsistent");
auto loop = builder.create<fir::DoLoopOp>(loc, lower, upper, stride,
false,
false);
builder.setInsertionPointToStart(loop.getBody());
return loop.getInductionVar();
}
hlfir::Entity finishArrayCtorLowering(mlir::Location loc,
fir::FirOpBuilder &builder) {
return moveStackAsArrayExpr(loc, builder);
}
};
using LooplessInlinedTempStrategy = InlinedTempStrategyImpl<false>;
using InlinedTempStrategy = InlinedTempStrategyImpl<true>;
class AsElementalStrategy : public StrategyBase {
public:
AsElementalStrategy(mlir::Location loc, fir::FirOpBuilder &builder,
Fortran::lower::StatementContext &stmtCtx,
Fortran::lower::SymMap &symMap,
fir::SequenceType declaredType, mlir::Value extent,
llvm::ArrayRef<mlir::Value> lengths)
: StrategyBase{stmtCtx, symMap}, shape{builder.genShape(loc, {extent})},
lengthParams{lengths.begin(), lengths.end()},
exprType{getExprType(declaredType)} {}
static hlfir::ExprType getExprType(fir::SequenceType declaredType) {
return hlfir::ExprType::get(declaredType.getContext(),
declaredType.getShape(),
declaredType.getEleTy(),
false);
}
mlir::Value startImpliedDo(mlir::Location loc, fir::FirOpBuilder &builder,
mlir::Value lower, mlir::Value upper,
mlir::Value stride) {
assert(!elementalOp && "expected only one implied-do");
mlir::Value one =
builder.createIntegerConstant(loc, builder.getIndexType(), 1);
elementalOp = builder.create<hlfir::ElementalOp>(
loc, exprType, shape,
nullptr, lengthParams, true);
builder.setInsertionPointToStart(elementalOp.getBody());
mlir::Value diff = builder.create<mlir::arith::SubIOp>(
loc, elementalOp.getIndices()[0], one);
mlir::Value mul = builder.create<mlir::arith::MulIOp>(loc, diff, stride);
mlir::Value add = builder.create<mlir::arith::AddIOp>(loc, lower, mul);
return add;
}
void pushValue(mlir::Location loc, fir::FirOpBuilder &builder,
hlfir::Entity value) {
assert(value.isScalar() && "cannot use hlfir.elemental with array values");
assert(elementalOp && "array constructor must contain an outer implied-do");
mlir::Value elementResult = value;
if (fir::isa_trivial(elementResult.getType()))
elementResult =
builder.createConvert(loc, exprType.getElementType(), elementResult);
stmtCtx.finalizeAndPop();
mlir::Operation *destroyOp = nullptr;
for (mlir::Operation *useOp : elementResult.getUsers())
if (mlir::isa<hlfir::DestroyOp>(useOp)) {
if (destroyOp)
fir::emitFatalError(loc,
"multiple DestroyOp's for ac-value expression");
destroyOp = useOp;
}
if (destroyOp)
destroyOp->erase();
builder.create<hlfir::YieldElementOp>(loc, elementResult);
}
virtual void endImpliedDoScope() override { symMap.popImpliedDoBinding(); }
hlfir::Entity finishArrayCtorLowering(mlir::Location loc,
fir::FirOpBuilder &builder) {
return hlfir::Entity{elementalOp};
}
private:
mlir::Value shape;
llvm::SmallVector<mlir::Value> lengthParams;
hlfir::ExprType exprType;
hlfir::ElementalOp elementalOp{};
};
class RuntimeTempStrategy : public StrategyBase {
static constexpr char tempName[] = ".tmp.arrayctor";
public:
RuntimeTempStrategy(mlir::Location loc, fir::FirOpBuilder &builder,
Fortran::lower::StatementContext &stmtCtx,
Fortran::lower::SymMap &symMap,
fir::SequenceType declaredType,
std::optional<mlir::Value> extent,
llvm::ArrayRef<mlir::Value> lengths,
bool missingLengthParameters)
: StrategyBase{stmtCtx, symMap},
arrayConstructorElementType{declaredType.getEleTy()} {
mlir::Type heapType = fir::HeapType::get(declaredType);
mlir::Type boxType = fir::BoxType::get(heapType);
allocatableTemp = builder.createTemporary(loc, boxType, tempName);
mlir::Value initialBoxValue;
if (extent && !missingLengthParameters) {
llvm::SmallVector<mlir::Value, 1> extents{*extent};
mlir::Value tempStorage = builder.createHeapTemporary(
loc, declaredType, tempName, extents, lengths);
mlir::Value shape = builder.genShape(loc, extents);
declare = builder.create<hlfir::DeclareOp>(
loc, tempStorage, tempName, shape, lengths,
nullptr, fir::FortranVariableFlagsAttr{});
initialBoxValue =
builder.createBox(loc, boxType, declare->getOriginalBase(), shape,
mlir::Value{}, lengths, {});
} else {
llvm::SmallVector<mlir::Value> emboxLengths(lengths.begin(),
lengths.end());
if (!extent)
extent = builder.createIntegerConstant(loc, builder.getIndexType(), 0);
if (missingLengthParameters) {
if (mlir::isa<fir::CharacterType>(declaredType.getEleTy()))
emboxLengths.push_back(builder.createIntegerConstant(
loc, builder.getCharacterLengthType(), 0));
else
TODO(loc,
"parametrized derived type array constructor without type-spec");
}
mlir::Value nullAddr = builder.createNullConstant(loc, heapType);
mlir::Value shape = builder.genShape(loc, {*extent});
initialBoxValue = builder.createBox(loc, boxType, nullAddr, shape,
mlir::Value{}, emboxLengths,
{});
}
builder.create<fir::StoreOp>(loc, initialBoxValue, allocatableTemp);
arrayConstructorVector = fir::runtime::genInitArrayConstructorVector(
loc, builder, allocatableTemp,
builder.createBool(loc, missingLengthParameters));
}
bool useSimplePushRuntime(hlfir::Entity value) {
return value.isScalar() &&
!mlir::isa<fir::CharacterType>(arrayConstructorElementType) &&
!fir::isRecordWithAllocatableMember(arrayConstructorElementType) &&
!fir::isRecordWithTypeParameters(arrayConstructorElementType);
}
void pushValue(mlir::Location loc, fir::FirOpBuilder &builder,
hlfir::Entity value) {
if (useSimplePushRuntime(value)) {
auto [addrExv, cleanUp] = hlfir::convertToAddress(
loc, builder, value, arrayConstructorElementType);
mlir::Value addr = fir::getBase(addrExv);
if (mlir::isa<fir::BaseBoxType>(addr.getType()))
addr = builder.create<fir::BoxAddrOp>(loc, addr);
fir::runtime::genPushArrayConstructorSimpleScalar(
loc, builder, arrayConstructorVector, addr);
if (cleanUp)
(*cleanUp)();
return;
}
auto [boxExv, cleanUp] =
hlfir::convertToBox(loc, builder, value, arrayConstructorElementType);
fir::runtime::genPushArrayConstructorValue(
loc, builder, arrayConstructorVector, fir::getBase(boxExv));
if (cleanUp)
(*cleanUp)();
}
mlir::Value startImpliedDo(mlir::Location loc, fir::FirOpBuilder &builder,
mlir::Value lower, mlir::Value upper,
mlir::Value stride) {
auto loop = builder.create<fir::DoLoopOp>(loc, lower, upper, stride,
false,
false);
builder.setInsertionPointToStart(loop.getBody());
return loop.getInductionVar();
}
hlfir::Entity finishArrayCtorLowering(mlir::Location loc,
fir::FirOpBuilder &builder) {
mlir::Value mustFree = builder.createBool(loc, true);
mlir::Value temp;
if (declare)
temp = declare->getBase();
else
temp = hlfir::derefPointersAndAllocatables(
loc, builder, hlfir::Entity{allocatableTemp});
auto hlfirExpr = builder.create<hlfir::AsExprOp>(loc, temp, mustFree);
return hlfir::Entity{hlfirExpr};
}
private:
mlir::Type arrayConstructorElementType;
mlir::Value allocatableTemp;
mlir::Value arrayConstructorVector;
std::optional<hlfir::DeclareOp> declare;
};
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 Fortran::common::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 Fortran::common::visit(
[&](auto &impl) {
return impl.startImpliedDo(loc, builder, lower, upper, stride);
},
implVariant);
}
hlfir::Entity finishArrayCtorLowering(mlir::Location loc,
fir::FirOpBuilder &builder) {
return Fortran::common::visit(
[&](auto &impl) { return impl.finishArrayCtorLowering(loc, builder); },
implVariant);
}
void startImpliedDoScope(llvm::StringRef doName, mlir::Value indexValue) {
Fortran::common::visit(
[&](auto &impl) {
return impl.startImpliedDoScope(doName, indexValue);
},
implVariant);
}
void endImpliedDoScope() {
Fortran::common::visit([&](auto &impl) { return impl.endImpliedDoScope(); },
implVariant);
}
private:
std::variant<InlinedTempStrategy, LooplessInlinedTempStrategy,
AsElementalStrategy, RuntimeTempStrategy>
implVariant;
};
}
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);
}
namespace {
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> &) {
return Fortran::lower::getFIRType(&converter.getMLIRContext(), T::category,
T::kind, {});
}
};
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) {
return Fortran::lower::translateDerivedTypeToFIRType(
converter, arrayCtorExpr.result().derivedTypeSpec());
}
};
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) {
llvm::SmallVector<Fortran::lower::LenParameterTy> typeLengths;
if (const Fortran::evaluate::ExtentExpr *lenExpr = arrayCtorExpr.LEN()) {
lengths.push_back(
lowerExtentExpr(loc, converter, symMap, stmtCtx, *lenExpr));
if (std::optional<std::int64_t> cstLen =
Fortran::evaluate::ToInt64(*lenExpr))
typeLengths.push_back(*cstLen);
}
return Fortran::lower::getFIRType(&converter.getMLIRContext(),
Fortran::common::TypeCategory::Character,
Kind, typeLengths);
}
};
}
static bool missingLengthParameters(mlir::Type elementType,
llvm::ArrayRef<mlir::Value> lengths) {
return (mlir::isa<fir::CharacterType>(elementType) ||
fir::isRecordWithTypeParameters(elementType)) &&
lengths.empty();
}
namespace {
struct ArrayCtorAnalysis {
template <typename T>
ArrayCtorAnalysis(
Fortran::evaluate::FoldingContext &,
const Fortran::evaluate::ArrayConstructor<T> &arrayCtorExpr);
bool isSingleImpliedDoWithOneScalarPureExpr() const {
return !anyArrayExpr && isPerfectLoopNest &&
innerNumberOfExprIfPrefectNest == 1 && depthIfPerfectLoopNest == 1 &&
innerExprIsPureIfPerfectNest;
}
bool anyImpliedDo = false;
bool anyArrayExpr = false;
bool isPerfectLoopNest = true;
bool innerExprIsPureIfPerfectNest = false;
std::int64_t innerNumberOfExprIfPrefectNest = 0;
std::int64_t depthIfPerfectLoopNest = 0;
};
}
template <typename T>
ArrayCtorAnalysis::ArrayCtorAnalysis(
Fortran::evaluate::FoldingContext &foldingContext,
const Fortran::evaluate::ArrayConstructor<T> &arrayCtorExpr) {
llvm::SmallVector<const Fortran::evaluate::ArrayConstructorValues<T> *>
arrayValueListStack{&arrayCtorExpr};
while (!arrayValueListStack.empty()) {
std::int64_t localNumberOfImpliedDo = 0;
std::int64_t localNumberOfExpr = 0;
const Fortran::evaluate::ArrayConstructorValues<T> *currentArrayValueList =
arrayValueListStack.pop_back_val();
for (const Fortran::evaluate::ArrayConstructorValue<T> &acValue :
*currentArrayValueList)
Fortran::common::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) {
if (isPerfectLoopNest) {
innerNumberOfExprIfPrefectNest = localNumberOfExpr;
if (localNumberOfExpr == 1)
innerExprIsPureIfPerfectNest = !Fortran::evaluate::FindImpureCall(
foldingContext, toEvExpr(std::get<Fortran::evaluate::Expr<T>>(
currentArrayValueList->begin()->u)));
}
} else if (localNumberOfImpliedDo == 1 && localNumberOfExpr == 0) {
++depthIfPerfectLoopNest;
} else {
isPerfectLoopNest = false;
}
}
}
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;
}
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();
mlir::Value extent;
fir::SequenceType::Extent typeExtent = fir::SequenceType::getUnknownExtent();
auto shapeExpr = Fortran::evaluate::GetContextFreeShape(
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)) {
extent = lowerExtentExpr(loc, converter, symMap, stmtCtx, extentExpr);
}
}
mlir::SmallVector<mlir::Value> lengths;
mlir::Type elementType = LengthAndTypeCollector<T>::collect(
loc, converter, arrayCtorExpr, symMap, stmtCtx, lengths);
ArrayCtorAnalysis analysis(converter.getFoldingContext(), arrayCtorExpr);
bool needToEvaluateOneExprToGetLengthParameters =
missingLengthParameters(elementType, lengths);
auto declaredType = fir::SequenceType::get({typeExtent}, elementType);
if (!extent || needToEvaluateOneExprToGetLengthParameters ||
analysis.anyArrayExpr ||
mlir::isa<fir::RecordType>(declaredType.getEleTy()))
return RuntimeTempStrategy(
loc, builder, stmtCtx, symMap, declaredType,
extent ? std::optional<mlir::Value>(extent) : std::nullopt, lengths,
needToEvaluateOneExprToGetLengthParameters);
if (analysis.isSingleImpliedDoWithOneScalarPureExpr())
return AsElementalStrategy(loc, builder, stmtCtx, symMap, declaredType,
extent, lengths);
if (analysis.anyImpliedDo)
return InlinedTempStrategy(loc, builder, stmtCtx, symMap, declaredType,
extent, lengths);
return LooplessInlinedTempStrategy(loc, builder, stmtCtx, symMap,
declaredType, extent, lengths);
}
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) {
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);
}
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);
arrayBuilder.startImpliedDoScope(toStringRef(impledDo.name()),
impliedDoIndexValue);
for (const auto &acValue : impledDo.values())
Fortran::common::visit(
[&](const auto &x) {
genAcValue(loc, converter, x, symMap, stmtCtx, arrayBuilder);
},
acValue.u);
arrayBuilder.endImpliedDoScope();
builder.restoreInsertionPoint(insertPt);
}
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();
auto arrayBuilder = selectArrayCtorLoweringStrategy(
loc, converter, arrayCtorExpr, symMap, stmtCtx);
for (const auto &acValue : arrayCtorExpr)
Fortran::common::visit(
[&](const auto &x) {
genAcValue(loc, converter, x, symMap, stmtCtx, arrayBuilder);
},
acValue.u);
hlfir::Entity hlfirExpr = arrayBuilder.finishArrayCtorLowering(loc, builder);
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, )