#include "flang/Lower/Allocatable.h"
#include "flang/Evaluate/tools.h"
#include "flang/Lower/AbstractConverter.h"
#include "flang/Lower/ConvertType.h"
#include "flang/Lower/ConvertVariable.h"
#include "flang/Lower/IterationSpace.h"
#include "flang/Lower/Mangler.h"
#include "flang/Lower/OpenACC.h"
#include "flang/Lower/PFTBuilder.h"
#include "flang/Lower/Runtime.h"
#include "flang/Lower/StatementContext.h"
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Optimizer/Dialect/CUF/CUFOps.h"
#include "flang/Optimizer/Dialect/FIROps.h"
#include "flang/Optimizer/Dialect/FIROpsSupport.h"
#include "flang/Optimizer/Support/FatalError.h"
#include "flang/Optimizer/Support/InternalNames.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Runtime/allocatable.h"
#include "flang/Runtime/pointer.h"
#include "flang/Semantics/tools.h"
#include "flang/Semantics/type.h"
#include "llvm/Support/CommandLine.h"
static llvm::cl::opt<bool> useAllocateRuntime(
"use-alloc-runtime",
llvm::cl::desc("Lower allocations to fortran runtime calls"),
llvm::cl::init(false));
static llvm::cl::opt<bool> useDescForMutableBox(
"use-desc-for-alloc",
llvm::cl::desc("Always use descriptors for POINTER and ALLOCATABLE"),
llvm::cl::init(true));
namespace {
struct ErrorManager {
void init(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
const Fortran::lower::SomeExpr *statExpr,
const Fortran::lower::SomeExpr *errMsgExpr) {
Fortran::lower::StatementContext stmtCtx;
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
hasStat = builder.createBool(loc, statExpr != nullptr);
statAddr = statExpr
? fir::getBase(converter.genExprAddr(loc, statExpr, stmtCtx))
: mlir::Value{};
errMsgAddr =
statExpr && errMsgExpr
? builder.createBox(loc,
converter.genExprAddr(loc, errMsgExpr, stmtCtx))
: builder.create<fir::AbsentOp>(
loc,
fir::BoxType::get(mlir::NoneType::get(builder.getContext())));
sourceFile = fir::factory::locationToFilename(builder, loc);
sourceLine = fir::factory::locationToLineNo(builder, loc,
builder.getIntegerType(32));
}
bool hasStatSpec() const { return static_cast<bool>(statAddr); }
void genStatCheck(fir::FirOpBuilder &builder, mlir::Location loc) {
if (statValue) {
mlir::Value zero =
builder.createIntegerConstant(loc, statValue.getType(), 0);
auto cmp = builder.create<mlir::arith::CmpIOp>(
loc, mlir::arith::CmpIPredicate::eq, statValue, zero);
auto ifOp = builder.create<fir::IfOp>(loc, cmp,
false);
builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
}
}
void assignStat(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Value stat) {
if (hasStatSpec()) {
assert(stat && "missing stat value");
mlir::Value castStat = builder.createConvert(
loc, fir::dyn_cast_ptrEleTy(statAddr.getType()), stat);
builder.create<fir::StoreOp>(loc, castStat, statAddr);
statValue = stat;
}
}
mlir::Value hasStat;
mlir::Value errMsgAddr;
mlir::Value sourceFile;
mlir::Value sourceLine;
private:
mlir::Value statAddr;
mlir::Value statValue;
};
using namespace Fortran::runtime;
static void genRuntimeSetBounds(fir::FirOpBuilder &builder, mlir::Location loc,
const fir::MutableBoxValue &box,
mlir::Value dimIndex, mlir::Value lowerBound,
mlir::Value upperBound) {
mlir::func::FuncOp callee =
box.isPointer()
? fir::runtime::getRuntimeFunc<mkRTKey(PointerSetBounds)>(loc,
builder)
: fir::runtime::getRuntimeFunc<mkRTKey(AllocatableSetBounds)>(
loc, builder);
llvm::SmallVector<mlir::Value> args{box.getAddr(), dimIndex, lowerBound,
upperBound};
llvm::SmallVector<mlir::Value> operands;
for (auto [fst, snd] : llvm::zip(args, callee.getFunctionType().getInputs()))
operands.emplace_back(builder.createConvert(loc, snd, fst));
builder.create<fir::CallOp>(loc, callee, operands);
}
static void genRuntimeInitCharacter(fir::FirOpBuilder &builder,
mlir::Location loc,
const fir::MutableBoxValue &box,
mlir::Value len, int64_t kind = 0) {
mlir::func::FuncOp callee =
box.isPointer()
? fir::runtime::getRuntimeFunc<mkRTKey(PointerNullifyCharacter)>(
loc, builder)
: fir::runtime::getRuntimeFunc<mkRTKey(
AllocatableInitCharacterForAllocate)>(loc, builder);
llvm::ArrayRef<mlir::Type> inputTypes = callee.getFunctionType().getInputs();
if (inputTypes.size() != 5)
fir::emitFatalError(
loc, "AllocatableInitCharacter runtime interface not as expected");
llvm::SmallVector<mlir::Value> args;
args.push_back(builder.createConvert(loc, inputTypes[0], box.getAddr()));
args.push_back(builder.createConvert(loc, inputTypes[1], len));
if (kind == 0)
kind = mlir::cast<fir::CharacterType>(box.getEleTy()).getFKind();
args.push_back(builder.createIntegerConstant(loc, inputTypes[2], kind));
int rank = box.rank();
args.push_back(builder.createIntegerConstant(loc, inputTypes[3], rank));
int corank = 0;
args.push_back(builder.createIntegerConstant(loc, inputTypes[4], corank));
builder.create<fir::CallOp>(loc, callee, args);
}
static mlir::Value genRuntimeAllocate(fir::FirOpBuilder &builder,
mlir::Location loc,
const fir::MutableBoxValue &box,
ErrorManager &errorManager) {
mlir::func::FuncOp callee =
box.isPointer()
? fir::runtime::getRuntimeFunc<mkRTKey(PointerAllocate)>(loc, builder)
: fir::runtime::getRuntimeFunc<mkRTKey(AllocatableAllocate)>(loc,
builder);
llvm::SmallVector<mlir::Value> args{
box.getAddr(), errorManager.hasStat, errorManager.errMsgAddr,
errorManager.sourceFile, errorManager.sourceLine};
llvm::SmallVector<mlir::Value> operands;
for (auto [fst, snd] : llvm::zip(args, callee.getFunctionType().getInputs()))
operands.emplace_back(builder.createConvert(loc, snd, fst));
return builder.create<fir::CallOp>(loc, callee, operands).getResult(0);
}
static mlir::Value genRuntimeAllocateSource(fir::FirOpBuilder &builder,
mlir::Location loc,
const fir::MutableBoxValue &box,
fir::ExtendedValue source,
ErrorManager &errorManager) {
mlir::func::FuncOp callee =
box.isPointer()
? fir::runtime::getRuntimeFunc<mkRTKey(PointerAllocateSource)>(
loc, builder)
: fir::runtime::getRuntimeFunc<mkRTKey(AllocatableAllocateSource)>(
loc, builder);
llvm::SmallVector<mlir::Value> args{
box.getAddr(), fir::getBase(source),
errorManager.hasStat, errorManager.errMsgAddr,
errorManager.sourceFile, errorManager.sourceLine};
llvm::SmallVector<mlir::Value> operands;
for (auto [fst, snd] : llvm::zip(args, callee.getFunctionType().getInputs()))
operands.emplace_back(builder.createConvert(loc, snd, fst));
return builder.create<fir::CallOp>(loc, callee, operands).getResult(0);
}
static void genRuntimeAllocateApplyMold(fir::FirOpBuilder &builder,
mlir::Location loc,
const fir::MutableBoxValue &box,
fir::ExtendedValue mold, int rank) {
mlir::func::FuncOp callee =
box.isPointer()
? fir::runtime::getRuntimeFunc<mkRTKey(PointerApplyMold)>(loc,
builder)
: fir::runtime::getRuntimeFunc<mkRTKey(AllocatableApplyMold)>(
loc, builder);
llvm::SmallVector<mlir::Value> args{
fir::factory::getMutableIRBox(builder, loc, box), fir::getBase(mold),
builder.createIntegerConstant(
loc, callee.getFunctionType().getInputs()[2], rank)};
llvm::SmallVector<mlir::Value> operands;
for (auto [fst, snd] : llvm::zip(args, callee.getFunctionType().getInputs()))
operands.emplace_back(builder.createConvert(loc, snd, fst));
builder.create<fir::CallOp>(loc, callee, operands);
}
static mlir::Value genRuntimeDeallocate(fir::FirOpBuilder &builder,
mlir::Location loc,
const fir::MutableBoxValue &box,
ErrorManager &errorManager,
mlir::Value declaredTypeDesc = {}) {
mlir::Value boxAddress = fir::factory::getMutableIRBox(builder, loc, box);
mlir::func::FuncOp callee;
llvm::SmallVector<mlir::Value> args;
llvm::SmallVector<mlir::Value> operands;
if (box.isPolymorphic() || box.isUnlimitedPolymorphic()) {
callee = box.isPointer()
? fir::runtime::getRuntimeFunc<mkRTKey(
PointerDeallocatePolymorphic)>(loc, builder)
: fir::runtime::getRuntimeFunc<mkRTKey(
AllocatableDeallocatePolymorphic)>(loc, builder);
if (!declaredTypeDesc)
declaredTypeDesc = builder.createNullConstant(loc);
operands = fir::runtime::createArguments(
builder, loc, callee.getFunctionType(), boxAddress, declaredTypeDesc,
errorManager.hasStat, errorManager.errMsgAddr, errorManager.sourceFile,
errorManager.sourceLine);
} else {
callee = box.isPointer()
? fir::runtime::getRuntimeFunc<mkRTKey(PointerDeallocate)>(
loc, builder)
: fir::runtime::getRuntimeFunc<mkRTKey(AllocatableDeallocate)>(
loc, builder);
operands = fir::runtime::createArguments(
builder, loc, callee.getFunctionType(), boxAddress,
errorManager.hasStat, errorManager.errMsgAddr, errorManager.sourceFile,
errorManager.sourceLine);
}
return builder.create<fir::CallOp>(loc, callee, operands).getResult(0);
}
static const Fortran::semantics::Symbol &
unwrapSymbol(const Fortran::parser::AllocateObject &allocObj) {
const Fortran::parser::Name &lastName =
Fortran::parser::GetLastName(allocObj);
assert(lastName.symbol);
return *lastName.symbol;
}
static fir::MutableBoxValue
genMutableBoxValue(Fortran::lower::AbstractConverter &converter,
mlir::Location loc,
const Fortran::parser::AllocateObject &allocObj) {
const Fortran::lower::SomeExpr *expr = Fortran::semantics::GetExpr(allocObj);
assert(expr && "semantic analysis failure");
return converter.genExprMutableBox(loc, *expr);
}
class AllocateStmtHelper {
public:
AllocateStmtHelper(Fortran::lower::AbstractConverter &converter,
const Fortran::parser::AllocateStmt &stmt,
mlir::Location loc)
: converter{converter}, builder{converter.getFirOpBuilder()}, stmt{stmt},
loc{loc} {}
void lower() {
visitAllocateOptions();
lowerAllocateLengthParameters();
errorManager.init(converter, loc, statExpr, errMsgExpr);
Fortran::lower::StatementContext stmtCtx;
if (sourceExpr)
sourceExv = converter.genExprBox(loc, *sourceExpr, stmtCtx);
if (moldExpr)
moldExv = converter.genExprBox(loc, *moldExpr, stmtCtx);
mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint();
for (const auto &allocation :
std::get<std::list<Fortran::parser::Allocation>>(stmt.t))
lowerAllocation(unwrapAllocation(allocation));
builder.restoreInsertionPoint(insertPt);
}
private:
struct Allocation {
const Fortran::parser::Allocation &alloc;
const Fortran::semantics::DeclTypeSpec &type;
bool hasCoarraySpec() const {
return std::get<std::optional<Fortran::parser::AllocateCoarraySpec>>(
alloc.t)
.has_value();
}
const Fortran::parser::AllocateObject &getAllocObj() const {
return std::get<Fortran::parser::AllocateObject>(alloc.t);
}
const Fortran::semantics::Symbol &getSymbol() const {
return unwrapSymbol(getAllocObj());
}
const std::list<Fortran::parser::AllocateShapeSpec> &getShapeSpecs() const {
return std::get<std::list<Fortran::parser::AllocateShapeSpec>>(alloc.t);
}
};
Allocation unwrapAllocation(const Fortran::parser::Allocation &alloc) {
const auto &allocObj = std::get<Fortran::parser::AllocateObject>(alloc.t);
const Fortran::semantics::Symbol &symbol = unwrapSymbol(allocObj);
assert(symbol.GetType());
return Allocation{alloc, *symbol.GetType()};
}
void visitAllocateOptions() {
for (const auto &allocOption :
std::get<std::list<Fortran::parser::AllocOpt>>(stmt.t))
Fortran::common::visit(
Fortran::common::visitors{
[&](const Fortran::parser::StatOrErrmsg &statOrErr) {
Fortran::common::visit(
Fortran::common::visitors{
[&](const Fortran::parser::StatVariable &statVar) {
statExpr = Fortran::semantics::GetExpr(statVar);
},
[&](const Fortran::parser::MsgVariable &errMsgVar) {
errMsgExpr = Fortran::semantics::GetExpr(errMsgVar);
},
},
statOrErr.u);
},
[&](const Fortran::parser::AllocOpt::Source &source) {
sourceExpr = Fortran::semantics::GetExpr(source.v.value());
},
[&](const Fortran::parser::AllocOpt::Mold &mold) {
moldExpr = Fortran::semantics::GetExpr(mold.v.value());
},
[&](const Fortran::parser::AllocOpt::Stream &stream) {
streamExpr = Fortran::semantics::GetExpr(stream.v.value());
},
[&](const Fortran::parser::AllocOpt::Pinned &pinned) {
pinnedExpr = Fortran::semantics::GetExpr(pinned.v.value());
},
},
allocOption.u);
}
void lowerAllocation(const Allocation &alloc) {
fir::MutableBoxValue boxAddr =
genMutableBoxValue(converter, loc, alloc.getAllocObj());
if (sourceExpr)
genSourceMoldAllocation(alloc, boxAddr, true);
else if (moldExpr)
genSourceMoldAllocation(alloc, boxAddr, false);
else
genSimpleAllocation(alloc, boxAddr);
}
static bool lowerBoundsAreOnes(const Allocation &alloc) {
for (const Fortran::parser::AllocateShapeSpec &shapeSpec :
alloc.getShapeSpecs())
if (std::get<0>(shapeSpec.t))
return false;
return true;
}
std::string mangleAlloc(const Allocation &alloc) {
return converter.mangleName(alloc.getSymbol()) + ".alloc";
}
void genInlinedAllocation(const Allocation &alloc,
const fir::MutableBoxValue &box) {
llvm::SmallVector<mlir::Value> lbounds;
llvm::SmallVector<mlir::Value> extents;
Fortran::lower::StatementContext stmtCtx;
mlir::Type idxTy = builder.getIndexType();
bool lBoundsAreOnes = lowerBoundsAreOnes(alloc);
mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
for (const Fortran::parser::AllocateShapeSpec &shapeSpec :
alloc.getShapeSpecs()) {
mlir::Value lb;
if (!lBoundsAreOnes) {
if (const std::optional<Fortran::parser::BoundExpr> &lbExpr =
std::get<0>(shapeSpec.t)) {
lb = fir::getBase(converter.genExprValue(
loc, Fortran::semantics::GetExpr(*lbExpr), stmtCtx));
lb = builder.createConvert(loc, idxTy, lb);
} else {
lb = one;
}
lbounds.emplace_back(lb);
}
mlir::Value ub = fir::getBase(converter.genExprValue(
loc, Fortran::semantics::GetExpr(std::get<1>(shapeSpec.t)), stmtCtx));
ub = builder.createConvert(loc, idxTy, ub);
if (lb) {
mlir::Value diff = builder.create<mlir::arith::SubIOp>(loc, ub, lb);
extents.emplace_back(
builder.create<mlir::arith::AddIOp>(loc, diff, one));
} else {
extents.emplace_back(ub);
}
}
fir::factory::genInlinedAllocation(builder, loc, box, lbounds, extents,
lenParams, mangleAlloc(alloc),
true);
}
void postAllocationAction(const Allocation &alloc) {
if (alloc.getSymbol().test(Fortran::semantics::Symbol::Flag::AccDeclare))
Fortran::lower::attachDeclarePostAllocAction(converter, builder,
alloc.getSymbol());
}
void genSimpleAllocation(const Allocation &alloc,
const fir::MutableBoxValue &box) {
if (!box.isDerived() && !errorManager.hasStatSpec() &&
!alloc.type.IsPolymorphic() && !alloc.hasCoarraySpec() &&
!useAllocateRuntime && !box.isPointer() &&
!Fortran::semantics::HasCUDAAttr(alloc.getSymbol())) {
genInlinedAllocation(alloc, box);
postAllocationAction(alloc);
return;
}
errorManager.genStatCheck(builder, loc);
genAllocateObjectInit(box);
if (alloc.hasCoarraySpec())
TODO(loc, "coarray: allocation of a coarray object");
if (alloc.type.IsPolymorphic())
genSetType(alloc, box, loc);
genSetDeferredLengthParameters(alloc, box);
genAllocateObjectBounds(alloc, box);
mlir::Value stat;
if (!Fortran::semantics::HasCUDAAttr(alloc.getSymbol()))
stat = genRuntimeAllocate(builder, loc, box, errorManager);
else
stat =
genCudaAllocate(builder, loc, box, errorManager, alloc.getSymbol());
fir::factory::syncMutableBoxFromIRBox(builder, loc, box);
postAllocationAction(alloc);
errorManager.assignStat(builder, loc, stat);
}
void lowerAllocateLengthParameters() {
const Fortran::semantics::DeclTypeSpec *typeSpec =
getIfAllocateStmtTypeSpec();
if (!typeSpec)
return;
if (const Fortran::semantics::DerivedTypeSpec *derived =
typeSpec->AsDerived())
if (Fortran::semantics::CountLenParameters(*derived) > 0)
TODO(loc, "setting derived type params in allocation");
if (typeSpec->category() ==
Fortran::semantics::DeclTypeSpec::Category::Character) {
Fortran::semantics::ParamValue lenParam =
typeSpec->characterTypeSpec().length();
if (Fortran::semantics::MaybeIntExpr intExpr = lenParam.GetExplicit()) {
Fortran::lower::StatementContext stmtCtx;
Fortran::lower::SomeExpr lenExpr{*intExpr};
lenParams.push_back(
fir::getBase(converter.genExprValue(loc, lenExpr, stmtCtx)));
}
}
}
void genSetDeferredLengthParameters(const Allocation &alloc,
const fir::MutableBoxValue &box) {
if (lenParams.empty())
return;
if (box.isCharacter())
genRuntimeInitCharacter(builder, loc, box, lenParams[0]);
if (box.isDerived())
TODO(loc, "derived type length parameters in allocate");
}
void genAllocateObjectInit(const fir::MutableBoxValue &box) {
if (box.isPointer()) {
mlir::Value nullPointer = fir::factory::createUnallocatedBox(
builder, loc, box.getBoxTy(), box.nonDeferredLenParams());
builder.create<fir::StoreOp>(loc, nullPointer, box.getAddr());
} else {
assert(box.isAllocatable() && "must be an allocatable");
fir::factory::getMutableIRBox(builder, loc, box);
}
}
void genAllocateObjectBounds(const Allocation &alloc,
const fir::MutableBoxValue &box) {
mlir::Type idxTy = builder.getIndexType();
mlir::Type i32Ty = builder.getIntegerType(32);
Fortran::lower::StatementContext stmtCtx;
for (const auto &iter : llvm::enumerate(alloc.getShapeSpecs())) {
mlir::Value lb;
const auto &bounds = iter.value().t;
if (const std::optional<Fortran::parser::BoundExpr> &lbExpr =
std::get<0>(bounds))
lb = fir::getBase(converter.genExprValue(
loc, Fortran::semantics::GetExpr(*lbExpr), stmtCtx));
else
lb = builder.createIntegerConstant(loc, idxTy, 1);
mlir::Value ub = fir::getBase(converter.genExprValue(
loc, Fortran::semantics::GetExpr(std::get<1>(bounds)), stmtCtx));
mlir::Value dimIndex =
builder.createIntegerConstant(loc, i32Ty, iter.index());
genRuntimeSetBounds(builder, loc, box, dimIndex, lb, ub);
}
if (sourceExpr && sourceExpr->Rank() > 0 &&
alloc.getShapeSpecs().size() == 0) {
mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
const auto *sourceBox = sourceExv.getBoxOf<fir::BoxValue>();
assert(sourceBox && "source expression should be lowered to one box");
for (int i = 0; i < sourceExpr->Rank(); ++i) {
auto dimVal = builder.createIntegerConstant(loc, idxTy, i);
auto dimInfo = builder.create<fir::BoxDimsOp>(
loc, idxTy, idxTy, idxTy, sourceBox->getAddr(), dimVal);
mlir::Value lb =
fir::factory::readLowerBound(builder, loc, sourceExv, i, one);
mlir::Value extent = dimInfo.getResult(1);
mlir::Value ub = builder.create<mlir::arith::SubIOp>(
loc, builder.create<mlir::arith::AddIOp>(loc, extent, lb), one);
mlir::Value dimIndex = builder.createIntegerConstant(loc, i32Ty, i);
genRuntimeSetBounds(builder, loc, box, dimIndex, lb, ub);
}
}
}
void genSourceMoldAllocation(const Allocation &alloc,
const fir::MutableBoxValue &box, bool isSource) {
fir::ExtendedValue exv = isSource ? sourceExv : moldExv;
;
errorManager.genStatCheck(builder, loc);
genAllocateObjectInit(box);
if (alloc.hasCoarraySpec())
TODO(loc, "coarray: allocation of a coarray object");
const bool isDeferredLengthCharacter =
box.isCharacter() && !box.hasNonDeferredLenParams();
if (lenParams.empty() && isDeferredLengthCharacter)
lenParams.push_back(fir::factory::readCharLen(builder, loc, exv));
if (!isSource || alloc.type.IsPolymorphic())
genRuntimeAllocateApplyMold(builder, loc, box, exv,
alloc.getSymbol().Rank());
if (isDeferredLengthCharacter)
genSetDeferredLengthParameters(alloc, box);
genAllocateObjectBounds(alloc, box);
mlir::Value stat;
if (Fortran::semantics::HasCUDAAttr(alloc.getSymbol()))
stat =
genCudaAllocate(builder, loc, box, errorManager, alloc.getSymbol());
else if (isSource)
stat = genRuntimeAllocateSource(builder, loc, box, exv, errorManager);
else
stat = genRuntimeAllocate(builder, loc, box, errorManager);
fir::factory::syncMutableBoxFromIRBox(builder, loc, box);
postAllocationAction(alloc);
errorManager.assignStat(builder, loc, stat);
}
void genInitDerived(const fir::MutableBoxValue &box, mlir::Value typeDescAddr,
int rank, int corank = 0) {
mlir::func::FuncOp callee =
box.isPointer()
? fir::runtime::getRuntimeFunc<mkRTKey(PointerNullifyDerived)>(
loc, builder)
: fir::runtime::getRuntimeFunc<mkRTKey(
AllocatableInitDerivedForAllocate)>(loc, builder);
llvm::ArrayRef<mlir::Type> inputTypes =
callee.getFunctionType().getInputs();
llvm::SmallVector<mlir::Value> args;
args.push_back(builder.createConvert(loc, inputTypes[0], box.getAddr()));
args.push_back(builder.createConvert(loc, inputTypes[1], typeDescAddr));
mlir::Value rankValue =
builder.createIntegerConstant(loc, inputTypes[2], rank);
mlir::Value corankValue =
builder.createIntegerConstant(loc, inputTypes[3], corank);
args.push_back(rankValue);
args.push_back(corankValue);
builder.create<fir::CallOp>(loc, callee, args);
}
void genInitIntrinsic(const fir::MutableBoxValue &box,
const TypeCategory category, int64_t kind, int rank,
int corank = 0) {
mlir::func::FuncOp callee =
box.isPointer()
? fir::runtime::getRuntimeFunc<mkRTKey(PointerNullifyIntrinsic)>(
loc, builder)
: fir::runtime::getRuntimeFunc<mkRTKey(
AllocatableInitIntrinsicForAllocate)>(loc, builder);
llvm::ArrayRef<mlir::Type> inputTypes =
callee.getFunctionType().getInputs();
llvm::SmallVector<mlir::Value> args;
args.push_back(builder.createConvert(loc, inputTypes[0], box.getAddr()));
mlir::Value categoryValue = builder.createIntegerConstant(
loc, inputTypes[1], static_cast<int32_t>(category));
mlir::Value kindValue =
builder.createIntegerConstant(loc, inputTypes[2], kind);
mlir::Value rankValue =
builder.createIntegerConstant(loc, inputTypes[3], rank);
mlir::Value corankValue =
builder.createIntegerConstant(loc, inputTypes[4], corank);
args.push_back(categoryValue);
args.push_back(kindValue);
args.push_back(rankValue);
args.push_back(corankValue);
builder.create<fir::CallOp>(loc, callee, args);
}
void genSetType(const Allocation &alloc, const fir::MutableBoxValue &box,
mlir::Location loc) {
const Fortran::semantics::DeclTypeSpec *typeSpec =
getIfAllocateStmtTypeSpec();
if (!typeSpec)
typeSpec = &alloc.type;
assert(typeSpec && "type spec missing for polymorphic allocation");
if (typeSpec->AsIntrinsic() &&
fir::isUnlimitedPolymorphicType(fir::getBase(box).getType())) {
if (typeSpec->AsIntrinsic()->category() == TypeCategory::Character) {
genRuntimeInitCharacter(
builder, loc, box, lenParams[0],
Fortran::evaluate::ToInt64(typeSpec->AsIntrinsic()->kind())
.value());
} else {
genInitIntrinsic(
box, typeSpec->AsIntrinsic()->category(),
Fortran::evaluate::ToInt64(typeSpec->AsIntrinsic()->kind()).value(),
alloc.getSymbol().Rank());
}
return;
}
if (!typeSpec->AsDerived())
return;
auto typeDescAddr = Fortran::lower::getTypeDescAddr(
converter, loc, typeSpec->derivedTypeSpec());
genInitDerived(box, typeDescAddr, alloc.getSymbol().Rank());
}
const Fortran::semantics::DeclTypeSpec *getIfAllocateStmtTypeSpec() const {
if (const auto &typeSpec =
std::get<std::optional<Fortran::parser::TypeSpec>>(stmt.t))
return typeSpec->declTypeSpec;
return nullptr;
}
mlir::Value genCudaAllocate(fir::FirOpBuilder &builder, mlir::Location loc,
const fir::MutableBoxValue &box,
ErrorManager &errorManager,
const Fortran::semantics::Symbol &sym) {
Fortran::lower::StatementContext stmtCtx;
cuf::DataAttributeAttr cudaAttr =
Fortran::lower::translateSymbolCUFDataAttribute(builder.getContext(),
sym);
mlir::Value errmsg = errMsgExpr ? errorManager.errMsgAddr : nullptr;
mlir::Value stream =
streamExpr
? fir::getBase(converter.genExprValue(loc, *streamExpr, stmtCtx))
: nullptr;
mlir::Value pinned =
pinnedExpr
? fir::getBase(converter.genExprAddr(loc, *pinnedExpr, stmtCtx))
: nullptr;
mlir::Value source = sourceExpr ? fir::getBase(sourceExv) : nullptr;
mlir::Type retTy = fir::runtime::getModel<int>()(builder.getContext());
return builder
.create<cuf::AllocateOp>(
loc, retTy, box.getAddr(), errmsg, stream, pinned, source, cudaAttr,
errorManager.hasStatSpec() ? builder.getUnitAttr() : nullptr)
.getResult();
}
Fortran::lower::AbstractConverter &converter;
fir::FirOpBuilder &builder;
const Fortran::parser::AllocateStmt &stmt;
const Fortran::lower::SomeExpr *sourceExpr{nullptr};
const Fortran::lower::SomeExpr *moldExpr{nullptr};
const Fortran::lower::SomeExpr *statExpr{nullptr};
const Fortran::lower::SomeExpr *errMsgExpr{nullptr};
const Fortran::lower::SomeExpr *pinnedExpr{nullptr};
const Fortran::lower::SomeExpr *streamExpr{nullptr};
llvm::SmallVector<mlir::Value> lenParams;
ErrorManager errorManager;
fir::ExtendedValue sourceExv;
fir::ExtendedValue moldExv;
mlir::Location loc;
};
}
void Fortran::lower::genAllocateStmt(
Fortran::lower::AbstractConverter &converter,
const Fortran::parser::AllocateStmt &stmt, mlir::Location loc) {
AllocateStmtHelper{converter, stmt, loc}.lower();
}
static void preDeallocationAction(Fortran::lower::AbstractConverter &converter,
fir::FirOpBuilder &builder,
mlir::Value beginOpValue,
const Fortran::semantics::Symbol &sym) {
if (sym.test(Fortran::semantics::Symbol::Flag::AccDeclare))
Fortran::lower::attachDeclarePreDeallocAction(converter, builder,
beginOpValue, sym);
}
static void postDeallocationAction(Fortran::lower::AbstractConverter &converter,
fir::FirOpBuilder &builder,
const Fortran::semantics::Symbol &sym) {
if (sym.test(Fortran::semantics::Symbol::Flag::AccDeclare))
Fortran::lower::attachDeclarePostDeallocAction(converter, builder, sym);
}
static mlir::Value genCudaDeallocate(fir::FirOpBuilder &builder,
mlir::Location loc,
const fir::MutableBoxValue &box,
ErrorManager &errorManager,
const Fortran::semantics::Symbol &sym) {
cuf::DataAttributeAttr cudaAttr =
Fortran::lower::translateSymbolCUFDataAttribute(builder.getContext(),
sym);
mlir::Value errmsg =
mlir::isa<fir::AbsentOp>(errorManager.errMsgAddr.getDefiningOp())
? nullptr
: errorManager.errMsgAddr;
mlir::Type retTy = fir::runtime::getModel<int>()(builder.getContext());
return builder
.create<cuf::DeallocateOp>(
loc, retTy, box.getAddr(), errmsg, cudaAttr,
errorManager.hasStatSpec() ? builder.getUnitAttr() : nullptr)
.getResult();
}
static mlir::Value
genDeallocate(fir::FirOpBuilder &builder,
Fortran::lower::AbstractConverter &converter, mlir::Location loc,
const fir::MutableBoxValue &box, ErrorManager &errorManager,
mlir::Value declaredTypeDesc = {},
const Fortran::semantics::Symbol *symbol = nullptr) {
bool isCudaSymbol = symbol && Fortran::semantics::HasCUDAAttr(*symbol);
if (!box.isDerived() && !box.isPolymorphic() && !box.hasAssumedRank() &&
!box.isUnlimitedPolymorphic() && !errorManager.hasStatSpec() &&
!useAllocateRuntime && !box.isPointer() && !isCudaSymbol) {
mlir::Value ret = fir::factory::genFreemem(builder, loc, box);
if (symbol)
postDeallocationAction(converter, builder, *symbol);
return ret;
}
errorManager.genStatCheck(builder, loc);
mlir::Value stat;
if (!isCudaSymbol)
stat =
genRuntimeDeallocate(builder, loc, box, errorManager, declaredTypeDesc);
else
stat = genCudaDeallocate(builder, loc, box, errorManager, *symbol);
fir::factory::syncMutableBoxFromIRBox(builder, loc, box);
if (symbol)
postDeallocationAction(converter, builder, *symbol);
errorManager.assignStat(builder, loc, stat);
return stat;
}
void Fortran::lower::genDeallocateBox(
Fortran::lower::AbstractConverter &converter,
const fir::MutableBoxValue &box, mlir::Location loc,
const Fortran::semantics::Symbol *sym, mlir::Value declaredTypeDesc) {
const Fortran::lower::SomeExpr *statExpr = nullptr;
const Fortran::lower::SomeExpr *errMsgExpr = nullptr;
ErrorManager errorManager;
errorManager.init(converter, loc, statExpr, errMsgExpr);
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
genDeallocate(builder, converter, loc, box, errorManager, declaredTypeDesc,
sym);
}
void Fortran::lower::genDeallocateIfAllocated(
Fortran::lower::AbstractConverter &converter,
const fir::MutableBoxValue &box, mlir::Location loc,
const Fortran::semantics::Symbol *sym) {
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
mlir::Value isAllocated =
fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, box);
builder.genIfThen(loc, isAllocated)
.genThen([&]() {
if (mlir::Type eleType = box.getEleTy();
mlir::isa<fir::RecordType>(eleType) && box.isPolymorphic()) {
mlir::Value declaredTypeDesc = builder.create<fir::TypeDescOp>(
loc, mlir::TypeAttr::get(eleType));
genDeallocateBox(converter, box, loc, sym, declaredTypeDesc);
} else {
genDeallocateBox(converter, box, loc, sym);
}
})
.end();
}
void Fortran::lower::genDeallocateStmt(
Fortran::lower::AbstractConverter &converter,
const Fortran::parser::DeallocateStmt &stmt, mlir::Location loc) {
const Fortran::lower::SomeExpr *statExpr = nullptr;
const Fortran::lower::SomeExpr *errMsgExpr = nullptr;
for (const Fortran::parser::StatOrErrmsg &statOrErr :
std::get<std::list<Fortran::parser::StatOrErrmsg>>(stmt.t))
Fortran::common::visit(
Fortran::common::visitors{
[&](const Fortran::parser::StatVariable &statVar) {
statExpr = Fortran::semantics::GetExpr(statVar);
},
[&](const Fortran::parser::MsgVariable &errMsgVar) {
errMsgExpr = Fortran::semantics::GetExpr(errMsgVar);
},
},
statOrErr.u);
ErrorManager errorManager;
errorManager.init(converter, loc, statExpr, errMsgExpr);
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint();
for (const Fortran::parser::AllocateObject &allocateObject :
std::get<std::list<Fortran::parser::AllocateObject>>(stmt.t)) {
const Fortran::semantics::Symbol &symbol = unwrapSymbol(allocateObject);
fir::MutableBoxValue box =
genMutableBoxValue(converter, loc, allocateObject);
mlir::Value declaredTypeDesc = {};
if (box.isPolymorphic()) {
mlir::Type eleType = box.getEleTy();
if (mlir::isa<fir::RecordType>(eleType))
if (const Fortran::semantics::DerivedTypeSpec *derivedTypeSpec =
symbol.GetType()->AsDerived()) {
declaredTypeDesc =
Fortran::lower::getTypeDescAddr(converter, loc, *derivedTypeSpec);
}
}
mlir::Value beginOpValue = genDeallocate(
builder, converter, loc, box, errorManager, declaredTypeDesc, &symbol);
preDeallocationAction(converter, builder, beginOpValue, symbol);
}
builder.restoreInsertionPoint(insertPt);
}
static inline bool
isNonContiguousArrayPointer(const Fortran::semantics::Symbol &sym) {
return Fortran::semantics::IsPointer(sym) && sym.Rank() != 0 &&
!sym.attrs().test(Fortran::semantics::Attr::CONTIGUOUS);
}
static inline bool isPolymorphicPointer(const Fortran::semantics::Symbol &sym) {
return Fortran::semantics::IsPointer(sym) &&
Fortran::semantics::IsPolymorphic(sym);
}
static inline bool
isPolymorphicAllocatable(const Fortran::semantics::Symbol &sym) {
return Fortran::semantics::IsAllocatable(sym) &&
Fortran::semantics::IsPolymorphic(sym);
}
static bool mayBeCapturedInInternalProc(const Fortran::semantics::Symbol &sym) {
const Fortran::semantics::Scope &owner = sym.owner();
Fortran::semantics::Scope::Kind kind = owner.kind();
if (kind == Fortran::semantics::Scope::Kind::Subprogram ||
kind == Fortran::semantics::Scope::Kind::MainProgram)
for (const Fortran::semantics::Scope &childScope : owner.children())
if (childScope.kind() == Fortran::semantics::Scope::Kind::Subprogram)
if (const Fortran::semantics::Symbol *childSym = childScope.symbol())
if (const auto *details =
childSym->detailsIf<Fortran::semantics::SubprogramDetails>())
if (!details->isInterface())
return true;
return false;
}
static fir::MutableProperties
createMutableProperties(Fortran::lower::AbstractConverter &converter,
mlir::Location loc,
const Fortran::lower::pft::Variable &var,
mlir::ValueRange nonDeferredParams, bool alwaysUseBox) {
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
const Fortran::semantics::Symbol &sym = var.getSymbol();
if (alwaysUseBox || var.isGlobal() || Fortran::semantics::IsDummy(sym) ||
Fortran::semantics::IsFunctionResult(sym) ||
sym.attrs().test(Fortran::semantics::Attr::VOLATILE) ||
isNonContiguousArrayPointer(sym) || useAllocateRuntime ||
useDescForMutableBox || mayBeCapturedInInternalProc(sym) ||
isPolymorphicPointer(sym) || isPolymorphicAllocatable(sym))
return {};
fir::MutableProperties mutableProperties;
std::string name = converter.mangleName(sym);
mlir::Type baseAddrTy = converter.genType(sym);
if (auto boxType = mlir::dyn_cast<fir::BaseBoxType>(baseAddrTy))
baseAddrTy = boxType.getEleTy();
mutableProperties.addr = builder.allocateLocal(
loc, baseAddrTy, name + ".addr", "",
std::nullopt, std::nullopt);
int rank = sym.Rank();
mlir::Type idxTy = builder.getIndexType();
for (decltype(rank) i = 0; i < rank; ++i) {
mlir::Value lboundVar = builder.allocateLocal(
loc, idxTy, name + ".lb" + std::to_string(i), "",
std::nullopt, std::nullopt);
mlir::Value extentVar = builder.allocateLocal(
loc, idxTy, name + ".ext" + std::to_string(i), "",
std::nullopt, std::nullopt);
mutableProperties.lbounds.emplace_back(lboundVar);
mutableProperties.extents.emplace_back(extentVar);
}
mlir::Type eleTy = baseAddrTy;
if (auto newTy = fir::dyn_cast_ptrEleTy(eleTy))
eleTy = newTy;
if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(eleTy))
eleTy = seqTy.getEleTy();
if (auto record = mlir::dyn_cast<fir::RecordType>(eleTy))
if (record.getNumLenParams() != 0)
TODO(loc, "deferred length type parameters.");
if (fir::isa_char(eleTy) && nonDeferredParams.empty()) {
mlir::Value lenVar =
builder.allocateLocal(loc, builder.getCharacterLengthType(),
name + ".len", "", std::nullopt,
std::nullopt);
mutableProperties.deferredParams.emplace_back(lenVar);
}
return mutableProperties;
}
fir::MutableBoxValue Fortran::lower::createMutableBox(
Fortran::lower::AbstractConverter &converter, mlir::Location loc,
const Fortran::lower::pft::Variable &var, mlir::Value boxAddr,
mlir::ValueRange nonDeferredParams, bool alwaysUseBox) {
fir::MutableProperties mutableProperties = createMutableProperties(
converter, loc, var, nonDeferredParams, alwaysUseBox);
fir::MutableBoxValue box(boxAddr, nonDeferredParams, mutableProperties);
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
if (!var.isGlobal() && !Fortran::semantics::IsDummy(var.getSymbol()))
fir::factory::disassociateMutableBox(builder, loc, box,
false);
return box;
}
bool Fortran::lower::isArraySectionWithoutVectorSubscript(
const Fortran::lower::SomeExpr &expr) {
return expr.Rank() > 0 && Fortran::evaluate::IsVariable(expr) &&
!Fortran::evaluate::UnwrapWholeSymbolDataRef(expr) &&
!Fortran::evaluate::HasVectorSubscript(expr);
}
void Fortran::lower::associateMutableBox(
Fortran::lower::AbstractConverter &converter, mlir::Location loc,
const fir::MutableBoxValue &box, const Fortran::lower::SomeExpr &source,
mlir::ValueRange lbounds, Fortran::lower::StatementContext &stmtCtx) {
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(source)) {
fir::factory::disassociateMutableBox(builder, loc, box);
return;
}
if (converter.getLoweringOptions().getLowerToHighLevelFIR()) {
fir::ExtendedValue rhs = converter.genExprAddr(loc, source, stmtCtx);
fir::factory::associateMutableBox(builder, loc, box, rhs, lbounds);
return;
}
fir::ExtendedValue rhs = isArraySectionWithoutVectorSubscript(source)
? converter.genExprBox(loc, source, stmtCtx)
: converter.genExprAddr(loc, source, stmtCtx);
fir::factory::associateMutableBox(builder, loc, box, rhs, lbounds);
}
bool Fortran::lower::isWholeAllocatable(const Fortran::lower::SomeExpr &expr) {
if (const Fortran::semantics::Symbol *sym =
Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(expr))
return Fortran::semantics::IsAllocatable(sym->GetUltimate());
return false;
}
bool Fortran::lower::isWholePointer(const Fortran::lower::SomeExpr &expr) {
if (const Fortran::semantics::Symbol *sym =
Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(expr))
return Fortran::semantics::IsPointer(sym->GetUltimate());
return false;
}
mlir::Value Fortran::lower::getAssumedCharAllocatableOrPointerLen(
fir::FirOpBuilder &builder, mlir::Location loc,
const Fortran::semantics::Symbol &sym, mlir::Value box) {
auto readLength = [&]() {
fir::BoxValue boxLoad =
builder.create<fir::LoadOp>(loc, fir::getBase(box)).getResult();
return fir::factory::readCharLen(builder, loc, boxLoad);
};
if (Fortran::semantics::IsOptional(sym)) {
mlir::IndexType idxTy = builder.getIndexType();
auto isPresent = builder.create<fir::IsPresentOp>(loc, builder.getI1Type(),
fir::getBase(box));
mlir::Value len =
builder.genIfOp(loc, {idxTy}, isPresent, true)
.genThen(
[&]() { builder.create<fir::ResultOp>(loc, readLength()); })
.genElse([&]() {
auto undef = builder.create<fir::UndefOp>(loc, idxTy);
builder.create<fir::ResultOp>(loc, undef.getResult());
})
.getResults()[0];
return len;
}
return readLength();
}
mlir::Value Fortran::lower::getTypeDescAddr(
AbstractConverter &converter, mlir::Location loc,
const Fortran::semantics::DerivedTypeSpec &typeSpec) {
mlir::Type typeDesc =
Fortran::lower::translateDerivedTypeToFIRType(converter, typeSpec);
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
return builder.create<fir::TypeDescOp>(loc, mlir::TypeAttr::get(typeDesc));
}