#include "flang/Lower/ConvertCall.h"
#include "flang/Lower/Allocatable.h"
#include "flang/Lower/ConvertExprToHLFIR.h"
#include "flang/Lower/ConvertProcedureDesignator.h"
#include "flang/Lower/ConvertVariable.h"
#include "flang/Lower/CustomIntrinsicCall.h"
#include "flang/Lower/HlfirIntrinsics.h"
#include "flang/Lower/StatementContext.h"
#include "flang/Lower/SymbolMap.h"
#include "flang/Optimizer/Builder/BoxValue.h"
#include "flang/Optimizer/Builder/Character.h"
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/HLFIRTools.h"
#include "flang/Optimizer/Builder/IntrinsicCall.h"
#include "flang/Optimizer/Builder/LowLevelIntrinsics.h"
#include "flang/Optimizer/Builder/MutableBox.h"
#include "flang/Optimizer/Builder/Runtime/Derived.h"
#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Optimizer/Dialect/CUF/CUFOps.h"
#include "flang/Optimizer/Dialect/FIROpsSupport.h"
#include "flang/Optimizer/HLFIR/HLFIROps.h"
#include "mlir/IR/IRMapping.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include <optional>
#define DEBUG_TYPE "flang-lower-expr"
static llvm::cl::opt<bool> useHlfirIntrinsicOps(
"use-hlfir-intrinsic-ops", llvm::cl::init(true),
llvm::cl::desc("Lower via HLFIR transformational intrinsic operations such "
"as hlfir.sum"));
static constexpr char tempResultName[] = ".tmp.func_result";
static fir::ExtendedValue toExtendedValue(mlir::Location loc, mlir::Value base,
llvm::ArrayRef<mlir::Value> extents,
llvm::ArrayRef<mlir::Value> lengths) {
mlir::Type type = base.getType();
if (mlir::isa<fir::BaseBoxType>(type))
return fir::BoxValue(base, {}, lengths, extents);
type = fir::unwrapRefType(type);
if (mlir::isa<fir::BaseBoxType>(type))
return fir::MutableBoxValue(base, lengths, {});
if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(type)) {
if (seqTy.getDimension() != extents.size())
fir::emitFatalError(loc, "incorrect number of extents for array");
if (mlir::isa<fir::CharacterType>(seqTy.getEleTy())) {
if (lengths.empty())
fir::emitFatalError(loc, "missing length for character");
assert(lengths.size() == 1);
return fir::CharArrayBoxValue(base, lengths[0], extents);
}
return fir::ArrayBoxValue(base, extents);
}
if (mlir::isa<fir::CharacterType>(type)) {
if (lengths.empty())
fir::emitFatalError(loc, "missing length for character");
assert(lengths.size() == 1);
return fir::CharBoxValue(base, lengths[0]);
}
return base;
}
static mlir::Value genRecordCPtrValueArg(fir::FirOpBuilder &builder,
mlir::Location loc, mlir::Value rec,
mlir::Type ty) {
mlir::Value cAddr = fir::factory::genCPtrOrCFunptrAddr(builder, loc, rec, ty);
mlir::Value cVal = builder.create<fir::LoadOp>(loc, cAddr);
return builder.createConvert(loc, cAddr.getType(), cVal);
}
[[maybe_unused]] static unsigned findHostAssocTuplePos(mlir::func::FuncOp fn) {
for (unsigned i = fn.getNumArguments(); i > 0; --i)
if (fn.getArgAttr(i - 1, fir::getHostAssocAttrName())) {
assert(i == fn.getNumArguments() && "tuple must be last");
return i - 1;
}
llvm_unreachable("anyFuncArgsHaveAttr failed");
}
mlir::Value
Fortran::lower::argumentHostAssocs(Fortran::lower::AbstractConverter &converter,
mlir::Value arg) {
if (auto addr = mlir::dyn_cast_or_null<fir::AddrOfOp>(arg.getDefiningOp())) {
auto &builder = converter.getFirOpBuilder();
if (auto funcOp = builder.getNamedFunction(addr.getSymbol()))
if (fir::anyFuncArgsHaveAttr(funcOp, fir::getHostAssocAttrName()))
return converter.hostAssocTupleValue();
}
return {};
}
static bool mustCastFuncOpToCopeWithImplicitInterfaceMismatch(
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
mlir::FunctionType callSiteType, mlir::FunctionType funcOpType) {
if (callSiteType.getNumResults() != funcOpType.getNumResults() ||
callSiteType.getNumInputs() != funcOpType.getNumInputs())
return true;
if (callSiteType.getResults() != funcOpType.getResults()) {
LLVM_DEBUG(mlir::emitWarning(
loc, "a return type mismatch is not standard compliant and may "
"lead to undefined behavior."));
return true;
}
if (converter.getLoweringOptions().getLowerToHighLevelFIR())
for (auto [actualType, dummyType] :
llvm::zip(callSiteType.getInputs(), funcOpType.getInputs()))
if (actualType != dummyType &&
!fir::ConvertOp::canBeConverted(actualType, dummyType))
return true;
return false;
}
static mlir::Value readDim3Value(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Value dim3Addr, llvm::StringRef comp) {
mlir::Type i32Ty = builder.getI32Type();
mlir::Type refI32Ty = fir::ReferenceType::get(i32Ty);
llvm::SmallVector<mlir::Value> lenParams;
mlir::Value designate = builder.create<hlfir::DesignateOp>(
loc, refI32Ty, dim3Addr, comp,
mlir::Value{}, hlfir::DesignateOp::Subscripts{},
mlir::ValueRange{}, std::nullopt,
mlir::Value{}, lenParams);
return hlfir::loadTrivialScalar(loc, builder, hlfir::Entity{designate});
}
static mlir::Value remapActualToDummyDescriptor(
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
Fortran::lower::SymMap &symMap,
const Fortran::lower::CallerInterface::PassedEntity &arg,
Fortran::lower::CallerInterface &caller, bool isBindcCall) {
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
mlir::IndexType idxTy = builder.getIndexType();
mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
Fortran::lower::StatementContext localStmtCtx;
auto lowerSpecExpr = [&](const auto &expr,
bool isAssumedSizeExtent) -> mlir::Value {
mlir::Value convertExpr = builder.createConvert(
loc, idxTy, fir::getBase(converter.genExprValue(expr, localStmtCtx)));
if (isAssumedSizeExtent)
return convertExpr;
return fir::factory::genMaxWithZero(builder, loc, convertExpr);
};
bool mapSymbols = caller.mustMapInterfaceSymbolsForDummyArgument(arg);
if (mapSymbols) {
symMap.pushScope();
const Fortran::semantics::Symbol *sym = caller.getDummySymbol(arg);
assert(sym && "call must have explicit interface to map interface symbols");
Fortran::lower::mapCallInterfaceSymbolsForDummyArgument(converter, caller,
symMap, *sym);
}
llvm::SmallVector<mlir::Value> extents;
llvm::SmallVector<mlir::Value> lengths;
mlir::Type dummyBoxType = caller.getDummyArgumentType(arg);
mlir::Type dummyBaseType = fir::unwrapPassByRefType(dummyBoxType);
if (mlir::isa<fir::SequenceType>(dummyBaseType))
caller.walkDummyArgumentExtents(
arg, [&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) {
extents.emplace_back(lowerSpecExpr(e, isAssumedSizeExtent));
});
mlir::Value shape;
if (!extents.empty()) {
if (isBindcCall) {
llvm::SmallVector<mlir::Value> lowerBounds(extents.size(), zero);
shape = builder.genShape(loc, lowerBounds, extents);
} else {
shape = builder.genShape(loc, extents);
}
}
hlfir::Entity explicitArgument = hlfir::Entity{caller.getInput(arg)};
mlir::Type dummyElementType = fir::unwrapSequenceType(dummyBaseType);
if (auto recType = llvm::dyn_cast<fir::RecordType>(dummyElementType))
if (recType.getNumLenParams() > 0)
TODO(loc, "sequence association of length parameterized derived type "
"dummy arguments");
if (fir::isa_char(dummyElementType))
lengths.emplace_back(hlfir::genCharLength(loc, builder, explicitArgument));
mlir::Value baseAddr =
hlfir::genVariableRawAddress(loc, builder, explicitArgument);
baseAddr = builder.createConvert(loc, fir::ReferenceType::get(dummyBaseType),
baseAddr);
mlir::Value mold;
if (fir::isPolymorphicType(dummyBoxType))
mold = explicitArgument;
mlir::Value remapped =
builder.create<fir::EmboxOp>(loc, dummyBoxType, baseAddr, shape,
mlir::Value{}, lengths, mold);
if (mapSymbols)
symMap.popScope();
return remapped;
}
static void remapActualToDummyDescriptors(
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
Fortran::lower::SymMap &symMap,
const Fortran::lower::PreparedActualArguments &loweredActuals,
Fortran::lower::CallerInterface &caller, bool isBindcCall) {
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
for (auto [preparedActual, arg] :
llvm::zip(loweredActuals, caller.getPassedArguments())) {
if (arg.isSequenceAssociatedDescriptor()) {
if (!preparedActual.value().handleDynamicOptional()) {
mlir::Value remapped = remapActualToDummyDescriptor(
loc, converter, symMap, arg, caller, isBindcCall);
caller.placeInput(arg, remapped);
} else {
mlir::Type dummyType = caller.getDummyArgumentType(arg);
mlir::Value isPresent = preparedActual.value().getIsPresent();
auto &argLambdaCapture = arg;
mlir::Value remapped =
builder
.genIfOp(loc, {dummyType}, isPresent,
true)
.genThen([&]() {
mlir::Value newBox = remapActualToDummyDescriptor(
loc, converter, symMap, argLambdaCapture, caller,
isBindcCall);
builder.create<fir::ResultOp>(loc, newBox);
})
.genElse([&]() {
mlir::Value absent =
builder.create<fir::AbsentOp>(loc, dummyType);
builder.create<fir::ResultOp>(loc, absent);
})
.getResults()[0];
caller.placeInput(arg, remapped);
}
}
}
}
std::pair<fir::ExtendedValue, bool> Fortran::lower::genCallOpAndResult(
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
Fortran::lower::CallerInterface &caller, mlir::FunctionType callSiteType,
std::optional<mlir::Type> resultType, bool isElemental) {
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
bool mustPopSymMap = false;
if (caller.mustMapInterfaceSymbolsForResult()) {
symMap.pushScope();
mustPopSymMap = true;
Fortran::lower::mapCallInterfaceSymbolsForResult(converter, caller, symMap);
}
mlir::Value funcPointer;
mlir::Value charFuncPointerLength;
if (const Fortran::evaluate::ProcedureDesignator *procDesignator =
caller.getIfIndirectCall()) {
if (mlir::Value passedArg = caller.getIfPassedArg()) {
funcPointer = Fortran::lower::derefPassProcPointerComponent(
loc, converter, *procDesignator, passedArg, symMap, stmtCtx);
} else {
Fortran::lower::SomeExpr expr{*procDesignator};
fir::ExtendedValue loweredProc =
converter.genExprAddr(loc, expr, stmtCtx);
funcPointer = fir::getBase(loweredProc);
if (const fir::CharBoxValue *charBox = loweredProc.getCharBox())
charFuncPointerLength = charBox->getLen();
}
}
mlir::IndexType idxTy = builder.getIndexType();
auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value {
mlir::Value convertExpr = builder.createConvert(
loc, idxTy, fir::getBase(converter.genExprValue(expr, stmtCtx)));
return fir::factory::genMaxWithZero(builder, loc, convertExpr);
};
llvm::SmallVector<mlir::Value> resultLengths;
auto allocatedResult = [&]() -> std::optional<fir::ExtendedValue> {
llvm::SmallVector<mlir::Value> extents;
llvm::SmallVector<mlir::Value> lengths;
if (!caller.callerAllocateResult())
return {};
mlir::Type type = caller.getResultStorageType();
if (mlir::isa<fir::SequenceType>(type))
caller.walkResultExtents(
[&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) {
assert(!isAssumedSizeExtent && "result cannot be assumed-size");
extents.emplace_back(lowerSpecExpr(e));
});
caller.walkResultLengths(
[&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) {
assert(!isAssumedSizeExtent && "result cannot be assumed-size");
lengths.emplace_back(lowerSpecExpr(e));
});
if (!mlir::isa<fir::BoxType>(type)) {
if (fir::isa_char(fir::unwrapSequenceType(type)) && lengths.empty()) {
if (!charFuncPointerLength)
fir::emitFatalError(loc, "failed to retrieve character function "
"length while calling it");
lengths.push_back(charFuncPointerLength);
}
resultLengths = lengths;
}
if (!extents.empty() || !lengths.empty()) {
auto *bldr = &converter.getFirOpBuilder();
auto stackSaveFn = fir::factory::getLlvmStackSave(builder);
auto stackSaveSymbol = bldr->getSymbolRefAttr(stackSaveFn.getName());
mlir::Value sp;
fir::CallOp call = bldr->create<fir::CallOp>(
loc, stackSaveFn.getFunctionType().getResults(), stackSaveSymbol,
mlir::ValueRange{});
if (call.getNumResults() != 0)
sp = call.getResult(0);
stmtCtx.attachCleanup([bldr, loc, sp]() {
auto stackRestoreFn = fir::factory::getLlvmStackRestore(*bldr);
auto stackRestoreSymbol =
bldr->getSymbolRefAttr(stackRestoreFn.getName());
bldr->create<fir::CallOp>(loc,
stackRestoreFn.getFunctionType().getResults(),
stackRestoreSymbol, mlir::ValueRange{sp});
});
}
mlir::Value temp =
builder.createTemporary(loc, type, ".result", extents, resultLengths);
return toExtendedValue(loc, temp, extents, lengths);
}();
if (mustPopSymMap)
symMap.popScope();
mlir::Value arrayResultShape;
if (allocatedResult) {
if (std::optional<Fortran::lower::CallInterface<
Fortran::lower::CallerInterface>::PassedEntity>
resultArg = caller.getPassedResult()) {
if (resultArg->passBy == PassBy::AddressAndLength)
caller.placeAddressAndLengthInput(*resultArg,
fir::getBase(*allocatedResult),
fir::getLen(*allocatedResult));
else if (resultArg->passBy == PassBy::BaseAddress)
caller.placeInput(*resultArg, fir::getBase(*allocatedResult));
else
fir::emitFatalError(
loc, "only expect character scalar result to be passed by ref");
} else {
assert(caller.mustSaveResult());
arrayResultShape = allocatedResult->match(
[&](const fir::CharArrayBoxValue &) {
return builder.createShape(loc, *allocatedResult);
},
[&](const fir::ArrayBoxValue &) {
return builder.createShape(loc, *allocatedResult);
},
[&](const auto &) { return mlir::Value{}; });
}
}
mlir::SymbolRefAttr funcSymbolAttr;
bool addHostAssociations = false;
if (!funcPointer) {
mlir::FunctionType funcOpType = caller.getFuncOp().getFunctionType();
mlir::SymbolRefAttr symbolAttr =
builder.getSymbolRefAttr(caller.getMangledName());
if (callSiteType.getNumResults() == funcOpType.getNumResults() &&
callSiteType.getNumInputs() + 1 == funcOpType.getNumInputs() &&
fir::anyFuncArgsHaveAttr(caller.getFuncOp(),
fir::getHostAssocAttrName())) {
assert(funcOpType.getInput(findHostAssocTuplePos(caller.getFuncOp())) ==
converter.hostAssocTupleValue().getType());
addHostAssociations = true;
}
if (!addHostAssociations &&
mustCastFuncOpToCopeWithImplicitInterfaceMismatch(
loc, converter, callSiteType, funcOpType))
funcPointer = builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
else
funcSymbolAttr = symbolAttr;
if (caller.getFuncOp()->hasAttrOfType<mlir::UnitAttr>(
fir::FIROpsDialect::getFirRuntimeAttrName()))
LLVM_DEBUG(mlir::emitWarning(
loc,
llvm::Twine("function name '") +
llvm::Twine(symbolAttr.getLeafReference()) +
llvm::Twine("' conflicts with a runtime function name used by "
"Flang - this may lead to undefined behavior")));
}
mlir::FunctionType funcType =
funcPointer ? callSiteType : caller.getFuncOp().getFunctionType();
llvm::SmallVector<mlir::Value> operands;
if (funcPointer) {
operands.push_back(
mlir::isa<fir::BoxProcType>(funcPointer.getType())
? builder.create<fir::BoxAddrOp>(loc, funcType, funcPointer)
: builder.createConvert(loc, funcType, funcPointer));
}
bool callingImplicitInterface = caller.canBeCalledViaImplicitInterface();
for (auto [fst, snd] : llvm::zip(caller.getInputs(), funcType.getInputs())) {
mlir::Value cast;
auto *context = builder.getContext();
if (mlir::isa<fir::BoxProcType>(snd) &&
mlir::isa<mlir::FunctionType>(fst.getType())) {
auto funcTy =
mlir::FunctionType::get(context, std::nullopt, std::nullopt);
auto boxProcTy = builder.getBoxProcType(funcTy);
if (mlir::Value host = argumentHostAssocs(converter, fst)) {
cast = builder.create<fir::EmboxProcOp>(
loc, boxProcTy, llvm::ArrayRef<mlir::Value>{fst, host});
} else {
cast = builder.create<fir::EmboxProcOp>(loc, boxProcTy, fst);
}
} else {
mlir::Type fromTy = fir::unwrapRefType(fst.getType());
if (fir::isa_builtin_cptr_type(fromTy) &&
Fortran::lower::isCPtrArgByValueType(snd)) {
cast = genRecordCPtrValueArg(builder, loc, fst, fromTy);
} else if (fir::isa_derived(snd) && !fir::isa_derived(fst.getType())) {
TODO(loc, "derived type argument passed by value");
} else {
bool legacyLowering =
!converter.getLoweringOptions().getLowerToHighLevelFIR();
cast = builder.convertWithSemantics(loc, snd, fst,
callingImplicitInterface,
legacyLowering);
}
}
operands.push_back(cast);
}
if (addHostAssociations)
operands.push_back(converter.hostAssocTupleValue());
mlir::Value callResult;
unsigned callNumResults;
if (!caller.getCallDescription().chevrons().empty()) {
mlir::Type i32Ty = builder.getI32Type();
mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1);
mlir::Value grid_x, grid_y, grid_z;
if (caller.getCallDescription().chevrons()[0].GetType()->category() ==
Fortran::common::TypeCategory::Integer) {
grid_x = builder.createConvert(
loc, i32Ty,
fir::getBase(converter.genExprValue(
caller.getCallDescription().chevrons()[0], stmtCtx)));
grid_y = one;
grid_z = one;
} else {
auto dim3Addr = converter.genExprAddr(
caller.getCallDescription().chevrons()[0], stmtCtx);
grid_x = readDim3Value(builder, loc, fir::getBase(dim3Addr), "x");
grid_y = readDim3Value(builder, loc, fir::getBase(dim3Addr), "y");
grid_z = readDim3Value(builder, loc, fir::getBase(dim3Addr), "z");
}
mlir::Value block_x, block_y, block_z;
if (caller.getCallDescription().chevrons()[1].GetType()->category() ==
Fortran::common::TypeCategory::Integer) {
block_x = builder.createConvert(
loc, i32Ty,
fir::getBase(converter.genExprValue(
caller.getCallDescription().chevrons()[1], stmtCtx)));
block_y = one;
block_z = one;
} else {
auto dim3Addr = converter.genExprAddr(
caller.getCallDescription().chevrons()[1], stmtCtx);
block_x = readDim3Value(builder, loc, fir::getBase(dim3Addr), "x");
block_y = readDim3Value(builder, loc, fir::getBase(dim3Addr), "y");
block_z = readDim3Value(builder, loc, fir::getBase(dim3Addr), "z");
}
mlir::Value bytes;
if (caller.getCallDescription().chevrons().size() > 2)
bytes = builder.createConvert(
loc, i32Ty,
fir::getBase(converter.genExprValue(
caller.getCallDescription().chevrons()[2], stmtCtx)));
mlir::Value stream;
if (caller.getCallDescription().chevrons().size() > 3)
stream = builder.createConvert(
loc, i32Ty,
fir::getBase(converter.genExprValue(
caller.getCallDescription().chevrons()[3], stmtCtx)));
builder.create<cuf::KernelLaunchOp>(
loc, funcType.getResults(), funcSymbolAttr, grid_x, grid_y, grid_z,
block_x, block_y, block_z, bytes, stream, operands);
callNumResults = 0;
} else if (caller.requireDispatchCall()) {
const auto &ultimateSymbol =
caller.getCallDescription().proc().GetSymbol()->GetUltimate();
std::string procName = ultimateSymbol.name().ToString();
if (const auto &binding{
ultimateSymbol.get<Fortran::semantics::ProcBindingDetails>()};
binding.numPrivatesNotOverridden() > 0)
procName += "."s + std::to_string(binding.numPrivatesNotOverridden());
fir::DispatchOp dispatch;
if (std::optional<unsigned> passArg = caller.getPassArgIndex()) {
dispatch = builder.create<fir::DispatchOp>(
loc, funcType.getResults(), builder.getStringAttr(procName),
caller.getInputs()[*passArg], operands,
builder.getI32IntegerAttr(*passArg));
} else {
const Fortran::evaluate::Component *component =
caller.getCallDescription().proc().GetComponent();
assert(component && "expect component for type-bound procedure call.");
fir::ExtendedValue dataRefValue = Fortran::lower::convertDataRefToValue(
loc, converter, component->base(), symMap, stmtCtx);
mlir::Value passObject = fir::getBase(dataRefValue);
if (fir::isa_ref_type(passObject.getType()))
passObject = builder.create<fir::LoadOp>(loc, passObject);
dispatch = builder.create<fir::DispatchOp>(
loc, funcType.getResults(), builder.getStringAttr(procName),
passObject, operands, nullptr);
}
callNumResults = dispatch.getNumResults();
if (callNumResults != 0)
callResult = dispatch.getResult(0);
} else {
auto call = builder.create<fir::CallOp>(loc, funcType.getResults(),
funcSymbolAttr, operands);
if (caller.characterize().IsBindC())
call.setIsBindC(true);
callNumResults = call.getNumResults();
if (callNumResults != 0)
callResult = call.getResult(0);
}
if (caller.mustSaveResult()) {
assert(allocatedResult.has_value());
builder.create<fir::SaveResultOp>(loc, callResult,
fir::getBase(*allocatedResult),
arrayResultShape, resultLengths);
}
if (allocatedResult) {
allocatedResult->match(
[&](const fir::MutableBoxValue &box) {
if (box.isAllocatable()) {
fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
stmtCtx.attachCleanup([bldr, loc, box]() {
fir::factory::genFreememIfAllocated(*bldr, loc, box);
});
}
},
[](const auto &) {});
bool resultIsFinalized = false;
std::optional<Fortran::evaluate::DynamicType> retTy =
caller.getCallDescription().proc().GetType();
if (!isElemental && !fir::isPointerType(funcType.getResults()[0]) &&
retTy &&
(retTy->category() == Fortran::common::TypeCategory::Derived ||
retTy->IsPolymorphic() || retTy->IsUnlimitedPolymorphic())) {
if (retTy->IsPolymorphic() || retTy->IsUnlimitedPolymorphic()) {
auto *bldr = &converter.getFirOpBuilder();
stmtCtx.attachCleanup([bldr, loc, allocatedResult]() {
fir::runtime::genDerivedTypeDestroy(*bldr, loc,
fir::getBase(*allocatedResult));
});
resultIsFinalized = true;
} else {
const Fortran::semantics::DerivedTypeSpec &typeSpec =
retTy->GetDerivedTypeSpec();
if (Fortran::semantics::MayRequireFinalization(typeSpec) ||
hlfir::mayHaveAllocatableComponent(funcType.getResults()[0])) {
auto *bldr = &converter.getFirOpBuilder();
stmtCtx.attachCleanup([bldr, loc, allocatedResult]() {
mlir::Value box = bldr->createBox(loc, *allocatedResult);
fir::runtime::genDerivedTypeDestroy(*bldr, loc, box);
});
resultIsFinalized = true;
}
}
}
return {*allocatedResult, resultIsFinalized};
}
if (!resultType)
return {fir::ExtendedValue{mlir::Value{}}, false};
assert(callNumResults == 1 && "Expected exactly one result in FUNCTION call");
(void)callNumResults;
if (caller.characterize().IsBindC() &&
mlir::isa<fir::CharacterType>(funcType.getResults()[0])) {
fir::CharacterType charTy =
mlir::dyn_cast<fir::CharacterType>(funcType.getResults()[0]);
mlir::Value len = builder.createIntegerConstant(
loc, builder.getCharacterLengthType(), charTy.getLen());
return {fir::CharBoxValue{callResult, len}, false};
}
return {callResult, false};
}
static hlfir::EntityWithAttributes genStmtFunctionRef(
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
const Fortran::evaluate::ProcedureRef &procRef) {
const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol();
assert(symbol && "expected symbol in ProcedureRef of statement functions");
const auto &details = symbol->get<Fortran::semantics::SubprogramDetails>();
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
symMap.pushScope();
llvm::SmallVector<hlfir::AssociateOp> exprAssociations;
for (auto [arg, bind] : llvm::zip(details.dummyArgs(), procRef.arguments())) {
assert(arg && "alternate return in statement function");
assert(bind && "optional argument in statement function");
const auto *expr = bind->UnwrapExpr();
assert(expr && "assumed type used as statement function argument");
hlfir::EntityWithAttributes loweredArg = Fortran::lower::convertExprToHLFIR(
loc, converter, *expr, symMap, stmtCtx);
fir::FortranVariableOpInterface variableIface = loweredArg.getIfVariable();
if (!variableIface) {
mlir::Type argType = converter.genType(*arg);
auto associate = hlfir::genAssociateExpr(
loc, builder, loweredArg, argType, toStringRef(arg->name()));
exprAssociations.push_back(associate);
variableIface = associate;
}
const Fortran::semantics::DeclTypeSpec *type = arg->GetType();
if (type &&
type->category() == Fortran::semantics::DeclTypeSpec::Character) {
symMap.addSymbol(*arg, variableIface.getBase());
Fortran::lower::mapSymbolAttributes(converter, *arg, symMap, stmtCtx);
} else {
symMap.addVariableDefinition(*arg, variableIface);
}
}
for (const Fortran::semantics::SymbolRef &sym :
Fortran::evaluate::CollectSymbols(*details.stmtFunction()))
if (const auto *details =
sym->detailsIf<Fortran::semantics::HostAssocDetails>())
converter.copySymbolBinding(details->symbol(), sym);
hlfir::Entity result = Fortran::lower::convertExprToHLFIR(
loc, converter, details.stmtFunction().value(), symMap, stmtCtx);
symMap.popScope();
result = hlfir::loadTrivialScalar(loc, builder, result);
if (result.isVariable())
result = hlfir::Entity{builder.create<hlfir::AsExprOp>(loc, result)};
for (auto associate : exprAssociations)
builder.create<hlfir::EndAssociateOp>(loc, associate);
return hlfir::EntityWithAttributes{result};
}
namespace {
struct CallContext {
CallContext(const Fortran::evaluate::ProcedureRef &procRef,
std::optional<mlir::Type> resultType, mlir::Location loc,
Fortran::lower::AbstractConverter &converter,
Fortran::lower::SymMap &symMap,
Fortran::lower::StatementContext &stmtCtx)
: procRef{procRef}, converter{converter}, symMap{symMap},
stmtCtx{stmtCtx}, resultType{resultType}, loc{loc} {}
fir::FirOpBuilder &getBuilder() { return converter.getFirOpBuilder(); }
std::string getProcedureName() const {
if (const Fortran::semantics::Symbol *sym = procRef.proc().GetSymbol())
return sym->GetUltimate().name().ToString();
return procRef.proc().GetName();
}
bool isElementalProcWithArrayArgs() const {
if (procRef.IsElemental())
for (const std::optional<Fortran::evaluate::ActualArgument> &arg :
procRef.arguments())
if (arg && arg->Rank() != 0)
return true;
return false;
}
bool isStatementFunctionCall() const {
if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
if (const auto *details =
symbol->detailsIf<Fortran::semantics::SubprogramDetails>())
return details->stmtFunction().has_value();
return false;
}
bool isBindcCall() const {
if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
return Fortran::semantics::IsBindCProcedure(*symbol);
return false;
}
const Fortran::evaluate::ProcedureRef &procRef;
Fortran::lower::AbstractConverter &converter;
Fortran::lower::SymMap &symMap;
Fortran::lower::StatementContext &stmtCtx;
std::optional<mlir::Type> resultType;
mlir::Location loc;
};
using ExvAndCleanup =
std::pair<fir::ExtendedValue, std::optional<hlfir::CleanupFunction>>;
}
static hlfir::EntityWithAttributes
extendedValueToHlfirEntity(mlir::Location loc, fir::FirOpBuilder &builder,
const fir::ExtendedValue &exv,
llvm::StringRef name) {
mlir::Value firBase = fir::getBase(exv);
mlir::Type firBaseTy = firBase.getType();
if (fir::isa_trivial(firBaseTy))
return hlfir::EntityWithAttributes{firBase};
if (auto charTy = mlir::dyn_cast<fir::CharacterType>(firBase.getType())) {
hlfir::Entity storage{builder.createTemporary(loc, charTy)};
builder.create<fir::StoreOp>(loc, firBase, storage);
auto asExpr = builder.create<hlfir::AsExprOp>(
loc, storage, builder.createBool(loc, false));
return hlfir::EntityWithAttributes{asExpr.getResult()};
}
return hlfir::genDeclare(loc, builder, exv, name,
fir::FortranVariableFlagsAttr{});
}
namespace {
struct CallCleanUp {
struct CopyIn {
void genCleanUp(mlir::Location loc, fir::FirOpBuilder &builder) {
builder.create<hlfir::CopyOutOp>(loc, tempBox, wasCopied, copyBackVar);
}
mlir::Value tempBox;
mlir::Value wasCopied;
mlir::Value copyBackVar;
};
struct ExprAssociate {
void genCleanUp(mlir::Location loc, fir::FirOpBuilder &builder) {
builder.create<hlfir::EndAssociateOp>(loc, tempVar, mustFree);
}
mlir::Value tempVar;
mlir::Value mustFree;
};
void genCleanUp(mlir::Location loc, fir::FirOpBuilder &builder) {
Fortran::common::visit([&](auto &c) { c.genCleanUp(loc, builder); },
cleanUp);
}
std::variant<CopyIn, ExprAssociate> cleanUp;
};
struct PreparedDummyArgument {
void pushCopyInCleanUp(mlir::Value tempBox, mlir::Value wasCopied,
mlir::Value copyBackVar) {
cleanups.emplace_back(
CallCleanUp{CallCleanUp::CopyIn{tempBox, wasCopied, copyBackVar}});
}
void pushExprAssociateCleanUp(mlir::Value tempVar, mlir::Value wasCopied) {
cleanups.emplace_back(
CallCleanUp{CallCleanUp::ExprAssociate{tempVar, wasCopied}});
}
void pushExprAssociateCleanUp(hlfir::AssociateOp associate) {
mlir::Value hlfirBase = associate.getBase();
mlir::Value firBase = associate.getFirBase();
cleanups.emplace_back(CallCleanUp{CallCleanUp::ExprAssociate{
hlfir::mayHaveAllocatableComponent(hlfirBase.getType()) ? hlfirBase
: firBase,
associate.getMustFreeStrorageFlag()}});
}
mlir::Value dummy;
llvm::SmallVector<CallCleanUp, 2> cleanups;
};
struct ConditionallyPreparedDummy {
ConditionallyPreparedDummy(PreparedDummyArgument &preparedDummy) {
thenResultValues.push_back(preparedDummy.dummy);
for (const CallCleanUp &c : preparedDummy.cleanups) {
if (const auto *copyInCleanUp =
std::get_if<CallCleanUp::CopyIn>(&c.cleanUp)) {
thenResultValues.push_back(copyInCleanUp->wasCopied);
if (copyInCleanUp->copyBackVar)
thenResultValues.push_back(copyInCleanUp->copyBackVar);
} else {
const auto &exprAssociate =
std::get<CallCleanUp::ExprAssociate>(c.cleanUp);
thenResultValues.push_back(exprAssociate.tempVar);
thenResultValues.push_back(exprAssociate.mustFree);
}
}
}
llvm::SmallVector<mlir::Type> getIfResulTypes() const {
llvm::SmallVector<mlir::Type> types;
for (mlir::Value res : thenResultValues)
types.push_back(res.getType());
return types;
}
void genThenResult(mlir::Location loc, fir::FirOpBuilder &builder) const {
builder.create<fir::ResultOp>(loc, thenResultValues);
}
void genElseResult(mlir::Location loc, fir::FirOpBuilder &builder) const {
llvm::SmallVector<mlir::Value> elseResultValues;
mlir::Type i1Type = builder.getI1Type();
for (mlir::Value res : thenResultValues) {
mlir::Type type = res.getType();
if (type == i1Type)
elseResultValues.push_back(builder.createBool(loc, false));
else
elseResultValues.push_back(builder.genAbsentOp(loc, type));
}
builder.create<fir::ResultOp>(loc, elseResultValues);
}
PreparedDummyArgument
getPreparedDummy(fir::IfOp ifOp,
const PreparedDummyArgument &unconditionalDummy) {
PreparedDummyArgument preparedDummy;
preparedDummy.dummy = ifOp.getResults()[0];
for (const CallCleanUp &c : unconditionalDummy.cleanups) {
if (const auto *copyInCleanUp =
std::get_if<CallCleanUp::CopyIn>(&c.cleanUp)) {
mlir::Value copyBackVar;
if (copyInCleanUp->copyBackVar)
copyBackVar = ifOp.getResults().back();
preparedDummy.pushCopyInCleanUp(copyInCleanUp->tempBox,
ifOp.getResults()[1], copyBackVar);
} else {
preparedDummy.pushExprAssociateCleanUp(ifOp.getResults()[1],
ifOp.getResults()[2]);
}
}
return preparedDummy;
}
llvm::SmallVector<mlir::Value> thenResultValues;
};
}
static hlfir::Entity fixProcedureDummyMismatch(mlir::Location loc,
fir::FirOpBuilder &builder,
hlfir::Entity actual,
mlir::Type dummyType) {
if (mlir::isa<fir::BoxProcType>(actual.getType()) &&
fir::isCharacterProcedureTuple(dummyType)) {
mlir::Value length =
builder.create<fir::UndefOp>(loc, builder.getCharacterLengthType());
mlir::Value tuple = fir::factory::createCharacterProcedureTuple(
builder, loc, dummyType, actual, length);
return hlfir::Entity{tuple};
}
assert(fir::isCharacterProcedureTuple(actual.getType()) &&
mlir::isa<fir::BoxProcType>(dummyType) &&
"unsupported dummy procedure mismatch with the actual argument");
mlir::Value boxProc = fir::factory::extractCharacterProcedureTuple(
builder, loc, actual, false)
.first;
return hlfir::Entity{boxProc};
}
mlir::Value static getZeroLowerBounds(mlir::Location loc,
fir::FirOpBuilder &builder,
hlfir::Entity entity) {
assert(!entity.isAssumedRank() &&
"assumed-rank must use fir.rebox_assumed_rank");
if (entity.getRank() < 1)
return {};
mlir::Value zero =
builder.createIntegerConstant(loc, builder.getIndexType(), 0);
llvm::SmallVector<mlir::Value> lowerBounds(entity.getRank(), zero);
return builder.genShift(loc, lowerBounds);
}
static bool
isSimplyContiguous(const Fortran::evaluate::ActualArgument &arg,
Fortran::evaluate::FoldingContext &foldingContext) {
if (const auto *expr = arg.UnwrapExpr())
return Fortran::evaluate::IsSimplyContiguous(*expr, foldingContext);
const Fortran::semantics::Symbol *sym = arg.GetAssumedTypeDummy();
assert(sym &&
"expect ActualArguments to be expression or assumed-type symbols");
return sym->Rank() == 0 ||
Fortran::evaluate::IsSimplyContiguous(*sym, foldingContext);
}
static PreparedDummyArgument preparePresentUserCallActualArgument(
mlir::Location loc, fir::FirOpBuilder &builder,
const Fortran::lower::PreparedActualArgument &preparedActual,
mlir::Type dummyType,
const Fortran::lower::CallerInterface::PassedEntity &arg,
CallContext &callContext) {
Fortran::evaluate::FoldingContext &foldingContext =
callContext.converter.getFoldingContext();
hlfir::Entity actual = preparedActual.getActual(loc, builder);
if (hlfir::isFortranProcedureValue(dummyType)) {
if (actual.isProcedurePointer()) {
actual = hlfir::derefPointersAndAllocatables(loc, builder, actual);
return PreparedDummyArgument{actual, {}};
}
assert(actual.isProcedure());
if (!mlir::isa<fir::BoxProcType>(actual.getType()) &&
actual.getType() != dummyType)
actual = fixProcedureDummyMismatch(loc, builder, actual, dummyType);
return PreparedDummyArgument{actual, {}};
}
const bool ignoreTKRtype = arg.testTKR(Fortran::common::IgnoreTKR::Type);
const bool passingPolymorphicToNonPolymorphic =
actual.isPolymorphic() && !fir::isPolymorphicType(dummyType) &&
!ignoreTKRtype;
const bool mustSetDynamicTypeToDummyType =
passingPolymorphicToNonPolymorphic &&
(actual.isArray() || mlir::isa<fir::BaseBoxType>(dummyType));
const bool mustDoCopyInOut =
actual.isArray() && arg.mustBeMadeContiguous() &&
(passingPolymorphicToNonPolymorphic ||
!isSimplyContiguous(*arg.entity, foldingContext));
const bool actualIsAssumedRank = actual.isAssumedRank();
mlir::Type dummyTypeWithActualRank = dummyType;
if (auto baseBoxDummy = mlir::dyn_cast<fir::BaseBoxType>(dummyType)) {
if (baseBoxDummy.isAssumedRank() ||
arg.testTKR(Fortran::common::IgnoreTKR::Rank) ||
arg.isSequenceAssociatedDescriptor()) {
mlir::Type actualTy =
hlfir::getFortranElementOrSequenceType(actual.getType());
dummyTypeWithActualRank = baseBoxDummy.getBoxTypeWithNewShape(actualTy);
}
}
if (ignoreTKRtype)
dummyTypeWithActualRank = fir::changeElementType(
dummyTypeWithActualRank, actual.getFortranElementType(),
actual.isPolymorphic());
PreparedDummyArgument preparedDummy;
auto genCopyIn = [&](hlfir::Entity var, bool doCopyOut) -> hlfir::Entity {
auto baseBoxTy = mlir::dyn_cast<fir::BaseBoxType>(var.getType());
assert(baseBoxTy && "expect non simply contiguous variables to be boxes");
mlir::Type tempBoxType = baseBoxTy.getBoxTypeWithNewAttr(
fir::BaseBoxType::Attribute::Allocatable);
mlir::Value tempBox = builder.createTemporary(loc, tempBoxType);
auto copyIn = builder.create<hlfir::CopyInOp>(
loc, var, tempBox, mlir::Value{});
preparedDummy.pushCopyInCleanUp(copyIn.getTempBox(), copyIn.getWasCopied(),
doCopyOut ? copyIn.getVar()
: mlir::Value{});
return hlfir::Entity{copyIn.getCopiedIn()};
};
auto genSetDynamicTypeToDummyType = [&](hlfir::Entity var) -> hlfir::Entity {
fir::BaseBoxType boxType = fir::BoxType::get(
hlfir::getFortranElementOrSequenceType(dummyTypeWithActualRank));
if (actualIsAssumedRank)
return hlfir::Entity{builder.create<fir::ReboxAssumedRankOp>(
loc, boxType, var, fir::LowerBoundModifierAttribute::SetToOnes)};
mlir::Type actualTy =
hlfir::getFortranElementOrSequenceType(actual.getType());
boxType = boxType.getBoxTypeWithNewShape(actualTy);
return hlfir::Entity{builder.create<fir::ReboxOp>(loc, boxType, var,
mlir::Value{},
mlir::Value{})};
};
hlfir::Entity entity =
hlfir::derefPointersAndAllocatables(loc, builder, actual);
if (entity.isVariable()) {
if (mustSetDynamicTypeToDummyType)
entity = genSetDynamicTypeToDummyType(entity);
if (arg.hasValueAttribute() ||
entity.isParameter()) {
auto copy = builder.create<hlfir::AsExprOp>(loc, entity);
mlir::Type storageType = entity.getType();
mlir::NamedAttribute byRefAttr = fir::getAdaptToByRefAttr(builder);
hlfir::AssociateOp associate = hlfir::genAssociateExpr(
loc, builder, hlfir::Entity{copy}, storageType, "", byRefAttr);
entity = hlfir::Entity{associate.getBase()};
preparedDummy.pushExprAssociateCleanUp(associate);
} else if (mustDoCopyInOut) {
entity = genCopyIn(entity, arg.mayBeModifiedByCall());
}
} else {
const Fortran::lower::SomeExpr *expr = arg.entity->UnwrapExpr();
assert(expr && "expression actual argument cannot be an assumed type");
mlir::Type storageType = callContext.converter.genType(*expr);
mlir::NamedAttribute byRefAttr = fir::getAdaptToByRefAttr(builder);
hlfir::AssociateOp associate = hlfir::genAssociateExpr(
loc, builder, entity, storageType, "", byRefAttr);
entity = hlfir::Entity{associate.getBase()};
preparedDummy.pushExprAssociateCleanUp(associate);
if (mustSetDynamicTypeToDummyType) {
entity = genSetDynamicTypeToDummyType(entity);
entity = genCopyIn(entity, false);
}
}
mlir::Value addr;
if (mlir::isa<fir::BoxCharType>(dummyTypeWithActualRank)) {
addr = hlfir::genVariableBoxChar(loc, builder, entity);
} else if (mlir::isa<fir::BaseBoxType>(dummyTypeWithActualRank)) {
entity = hlfir::genVariableBox(loc, builder, entity);
fir::BaseBoxType actualBoxType =
mlir::cast<fir::BaseBoxType>(entity.getType());
mlir::Type boxEleType = actualBoxType.getEleTy();
const bool actualBoxHasAllocatableOrPointerFlag =
fir::isa_ref_type(boxEleType);
bool needsZeroLowerBounds = callContext.isBindcCall() && entity.isArray();
const bool actualBoxHasAddendum = fir::boxHasAddendum(actualBoxType);
const bool needToAddAddendum =
fir::isUnlimitedPolymorphicType(dummyTypeWithActualRank) &&
!actualBoxHasAddendum;
if (needToAddAddendum || actualBoxHasAllocatableOrPointerFlag ||
needsZeroLowerBounds) {
if (actualIsAssumedRank) {
auto lbModifier = needsZeroLowerBounds
? fir::LowerBoundModifierAttribute::SetToZeroes
: fir::LowerBoundModifierAttribute::SetToOnes;
entity = hlfir::Entity{builder.create<fir::ReboxAssumedRankOp>(
loc, dummyTypeWithActualRank, entity, lbModifier)};
} else {
mlir::Value shift{};
if (needsZeroLowerBounds)
shift = getZeroLowerBounds(loc, builder, entity);
entity = hlfir::Entity{builder.create<fir::ReboxOp>(
loc, dummyTypeWithActualRank, entity, shift,
mlir::Value{})};
}
}
addr = entity;
} else {
addr = hlfir::genVariableRawAddress(loc, builder, entity);
}
preparedDummy.dummy =
builder.createConvert(loc, dummyTypeWithActualRank, addr);
return preparedDummy;
}
static PreparedDummyArgument prepareUserCallActualArgument(
mlir::Location loc, fir::FirOpBuilder &builder,
const Fortran::lower::PreparedActualArgument &preparedActual,
mlir::Type dummyType,
const Fortran::lower::CallerInterface::PassedEntity &arg,
CallContext &callContext) {
if (!preparedActual.handleDynamicOptional())
return preparePresentUserCallActualArgument(loc, builder, preparedActual,
dummyType, arg, callContext);
mlir::Value isPresent = preparedActual.getIsPresent();
mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint();
auto badIfOp = builder.create<fir::IfOp>(loc, dummyType, isPresent,
false);
mlir::Block *preparationBlock = &badIfOp.getThenRegion().front();
builder.setInsertionPointToStart(preparationBlock);
PreparedDummyArgument unconditionalDummy =
preparePresentUserCallActualArgument(loc, builder, preparedActual,
dummyType, arg, callContext);
builder.restoreInsertionPoint(insertPt);
llvm::SmallVector<mlir::Type> ifOpResultTypes;
ConditionallyPreparedDummy conditionalDummy(unconditionalDummy);
auto ifOp = builder.create<fir::IfOp>(loc, conditionalDummy.getIfResulTypes(),
isPresent,
true);
preparationBlock->moveBefore(&ifOp.getThenRegion().back());
ifOp.getThenRegion().back().erase();
builder.setInsertionPointToEnd(&ifOp.getThenRegion().front());
conditionalDummy.genThenResult(loc, builder);
builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
conditionalDummy.genElseResult(loc, builder);
builder.setInsertionPointAfter(ifOp);
PreparedDummyArgument result =
conditionalDummy.getPreparedDummy(ifOp, unconditionalDummy);
badIfOp->erase();
return result;
}
static PreparedDummyArgument prepareProcedurePointerActualArgument(
mlir::Location loc, fir::FirOpBuilder &builder,
const Fortran::lower::PreparedActualArgument &preparedActual,
mlir::Type dummyType,
const Fortran::lower::CallerInterface::PassedEntity &arg,
CallContext &callContext) {
if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(
*arg.entity) &&
fir::isBoxProcAddressType(dummyType)) {
auto boxTy{Fortran::lower::getUntypedBoxProcType(builder.getContext())};
auto tempBoxProc{builder.createTemporary(loc, boxTy)};
hlfir::Entity nullBoxProc(
fir::factory::createNullBoxProc(builder, loc, boxTy));
builder.create<fir::StoreOp>(loc, nullBoxProc, tempBoxProc);
return PreparedDummyArgument{tempBoxProc, {}};
}
hlfir::Entity actual = preparedActual.getActual(loc, builder);
if (actual.isProcedurePointer())
return PreparedDummyArgument{actual, {}};
assert(actual.isProcedure());
auto tempBoxProc{builder.createTemporary(loc, actual.getType())};
builder.create<fir::StoreOp>(loc, actual, tempBoxProc);
return PreparedDummyArgument{tempBoxProc, {}};
}
void prepareUserCallArguments(
Fortran::lower::PreparedActualArguments &loweredActuals,
Fortran::lower::CallerInterface &caller, mlir::FunctionType callSiteType,
CallContext &callContext, llvm::SmallVector<CallCleanUp> &callCleanUps) {
using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
mlir::Location loc = callContext.loc;
bool mustRemapActualToDummyDescriptors = false;
fir::FirOpBuilder &builder = callContext.getBuilder();
for (auto [preparedActual, arg] :
llvm::zip(loweredActuals, caller.getPassedArguments())) {
mlir::Type argTy = callSiteType.getInput(arg.firArgument);
if (!preparedActual) {
caller.placeInput(arg, builder.genAbsentOp(loc, argTy));
continue;
}
switch (arg.passBy) {
case PassBy::Value: {
assert(!preparedActual->handleDynamicOptional() && "cannot be optional");
hlfir::Entity actual = preparedActual->getActual(loc, builder);
hlfir::Entity value = hlfir::loadTrivialScalar(loc, builder, actual);
mlir::Type eleTy = value.getFortranElementType();
if (fir::isa_builtin_cptr_type(eleTy)) {
if (value.isValue()) {
auto associate = hlfir::genAssociateExpr(loc, builder, value, eleTy,
"adapt.cptrbyval");
value = hlfir::Entity{genRecordCPtrValueArg(
builder, loc, associate.getFirBase(), eleTy)};
builder.create<hlfir::EndAssociateOp>(loc, associate);
} else {
value =
hlfir::Entity{genRecordCPtrValueArg(builder, loc, value, eleTy)};
}
} else if (fir::isa_derived(value.getFortranElementType()) ||
value.isCharacter()) {
auto [exv, cleanup] = hlfir::convertToValue(loc, builder, value);
mlir::Value loadedValue = fir::getBase(exv);
if (mlir::Type baseTy = fir::dyn_cast_ptrEleTy(loadedValue.getType()))
if (fir::isa_char(baseTy))
loadedValue = builder.createConvert(
loc, fir::ReferenceType::get(argTy), loadedValue);
if (fir::isa_ref_type(loadedValue.getType()))
loadedValue = builder.create<fir::LoadOp>(loc, loadedValue);
caller.placeInput(arg, loadedValue);
if (cleanup)
(*cleanup)();
break;
}
caller.placeInput(arg, builder.createConvert(loc, argTy, value));
} break;
case PassBy::BaseAddressValueAttribute:
case PassBy::CharBoxValueAttribute:
case PassBy::Box:
case PassBy::BaseAddress:
case PassBy::BoxChar: {
PreparedDummyArgument preparedDummy = prepareUserCallActualArgument(
loc, builder, *preparedActual, argTy, arg, callContext);
callCleanUps.append(preparedDummy.cleanups.rbegin(),
preparedDummy.cleanups.rend());
caller.placeInput(arg, preparedDummy.dummy);
if (arg.passBy == PassBy::Box)
mustRemapActualToDummyDescriptors |=
arg.isSequenceAssociatedDescriptor();
} break;
case PassBy::BoxProcRef: {
PreparedDummyArgument preparedDummy =
prepareProcedurePointerActualArgument(loc, builder, *preparedActual,
argTy, arg, callContext);
callCleanUps.append(preparedDummy.cleanups.rbegin(),
preparedDummy.cleanups.rend());
caller.placeInput(arg, preparedDummy.dummy);
} break;
case PassBy::AddressAndLength:
fir::emitFatalError(
loc, "unexpected PassBy::AddressAndLength for actual arguments");
break;
case PassBy::CharProcTuple: {
hlfir::Entity actual = preparedActual->getActual(loc, builder);
if (actual.isProcedurePointer())
actual = hlfir::derefPointersAndAllocatables(loc, builder, actual);
if (!fir::isCharacterProcedureTuple(actual.getType()))
actual = fixProcedureDummyMismatch(loc, builder, actual, argTy);
caller.placeInput(arg, actual);
} break;
case PassBy::MutableBox: {
const Fortran::lower::SomeExpr *expr = arg.entity->UnwrapExpr();
assert(expr && "cannot pass TYPE(*) to POINTER or ALLOCATABLE");
hlfir::Entity actual = preparedActual->getActual(loc, builder);
if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(
*expr)) {
mlir::Type boxTy = fir::dyn_cast_ptrEleTy(argTy);
assert(boxTy && mlir::isa<fir::BaseBoxType>(boxTy) &&
"must be a fir.box type");
mlir::Value boxStorage =
fir::factory::genNullBoxStorage(builder, loc, boxTy);
caller.placeInput(arg, boxStorage);
continue;
}
if (fir::isPointerType(argTy) &&
!Fortran::evaluate::IsObjectPointer(*expr)) {
auto dataTy = llvm::cast<fir::BaseBoxType>(fir::unwrapRefType(argTy));
fir::ExtendedValue actualExv = Fortran::lower::convertToAddress(
loc, callContext.converter, actual, callContext.stmtCtx,
hlfir::getFortranElementType(dataTy));
if (dataTy.isAssumedRank()) {
dataTy =
dataTy.getBoxTypeWithNewShape(fir::getBase(actualExv).getType());
}
mlir::Value irBox = builder.createTemporary(loc, dataTy);
fir::MutableBoxValue ptrBox(irBox,
mlir::ValueRange{},
{});
fir::factory::associateMutableBox(builder, loc, ptrBox, actualExv,
std::nullopt);
caller.placeInput(arg, irBox);
continue;
}
assert(actual.isMutableBox() && "actual must be a mutable box");
if (fir::isAllocatableType(argTy) && arg.isIntentOut() &&
callContext.isBindcCall()) {
auto [exv, cleanup] =
hlfir::translateToExtendedValue(loc, builder, actual);
const auto *mutableBox = exv.getBoxOf<fir::MutableBoxValue>();
assert(mutableBox && !cleanup && "expect allocatable");
Fortran::lower::genDeallocateIfAllocated(callContext.converter,
*mutableBox, loc);
}
caller.placeInput(arg, actual);
} break;
}
}
if (mustRemapActualToDummyDescriptors)
remapActualToDummyDescriptors(loc, callContext.converter,
callContext.symMap, loweredActuals, caller,
callContext.isBindcCall());
}
static std::optional<hlfir::EntityWithAttributes>
genUserCall(Fortran::lower::PreparedActualArguments &loweredActuals,
Fortran::lower::CallerInterface &caller,
mlir::FunctionType callSiteType, CallContext &callContext) {
mlir::Location loc = callContext.loc;
llvm::SmallVector<CallCleanUp> callCleanUps;
fir::FirOpBuilder &builder = callContext.getBuilder();
prepareUserCallArguments(loweredActuals, caller, callSiteType, callContext,
callCleanUps);
auto [result, resultIsFinalized] = Fortran::lower::genCallOpAndResult(
loc, callContext.converter, callContext.symMap, callContext.stmtCtx,
caller, callSiteType, callContext.resultType,
callContext.isElementalProcWithArrayArgs());
if (callContext.resultType &&
mlir::isa<fir::BoxProcType>(*callContext.resultType))
return hlfir::EntityWithAttributes(fir::getBase(result));
for (auto cleanUp : callCleanUps)
cleanUp.genCleanUp(loc, builder);
if (!fir::getBase(result))
return std::nullopt;
if (fir::isPointerType(fir::getBase(result).getType()))
return extendedValueToHlfirEntity(loc, builder, result, tempResultName);
if (!resultIsFinalized) {
hlfir::Entity resultEntity =
extendedValueToHlfirEntity(loc, builder, result, tempResultName);
resultEntity = loadTrivialScalar(loc, builder, resultEntity);
if (resultEntity.isVariable()) {
auto asExpr = builder.create<hlfir::AsExprOp>(
loc, resultEntity, builder.createBool(loc, false));
return hlfir::EntityWithAttributes{asExpr.getResult()};
}
return hlfir::EntityWithAttributes{resultEntity};
}
const auto *allocatable = result.getBoxOf<fir::MutableBoxValue>();
fir::ExtendedValue loadedResult =
allocatable
? fir::factory::genMutableBoxRead(builder, loc, *allocatable,
true,
false)
: result;
return extendedValueToHlfirEntity(loc, builder, loadedResult, tempResultName);
}
template <typename T>
static ExvAndCleanup genOptionalValue(fir::FirOpBuilder &builder,
mlir::Location loc, mlir::Type eleType,
T actualGetter, mlir::Value isPresent) {
return {builder
.genIfOp(loc, {eleType}, isPresent,
true)
.genThen([&]() {
hlfir::Entity entity = actualGetter(loc, builder);
assert(eleType == entity.getFortranElementType() &&
"result type mismatch in genOptionalValue");
assert(entity.isScalar() && fir::isa_trivial(eleType) &&
"must be a numerical or logical scalar");
mlir::Value val =
hlfir::loadTrivialScalar(loc, builder, entity);
builder.create<fir::ResultOp>(loc, val);
})
.genElse([&]() {
mlir::Value zero =
fir::factory::createZeroValue(builder, loc, eleType);
builder.create<fir::ResultOp>(loc, zero);
})
.getResults()[0],
std::nullopt};
}
static ExvAndCleanup genOptionalAddr(fir::FirOpBuilder &builder,
mlir::Location loc, hlfir::Entity entity,
mlir::Value isPresent) {
auto [exv, cleanup] = hlfir::translateToExtendedValue(loc, builder, entity);
if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>())
return {fir::factory::genMutableBoxRead(builder, loc, *box), cleanup};
return {exv, cleanup};
}
static ExvAndCleanup genOptionalBox(fir::FirOpBuilder &builder,
mlir::Location loc, hlfir::Entity entity,
mlir::Value isPresent) {
auto [exv, cleanup] = hlfir::translateToExtendedValue(loc, builder, entity);
if (exv.getBoxOf<fir::BoxValue>())
return {exv, cleanup};
fir::ExtendedValue newExv = exv;
if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>())
newExv = fir::factory::genMutableBoxRead(builder, loc, *box);
mlir::Value box = builder.createBox(loc, newExv);
mlir::Type boxType = box.getType();
auto absent = builder.create<fir::AbsentOp>(loc, boxType);
auto boxOrAbsent = builder.create<mlir::arith::SelectOp>(
loc, boxType, isPresent, box, absent);
return {fir::BoxValue(boxOrAbsent), cleanup};
}
static std::optional<hlfir::EntityWithAttributes> genCustomIntrinsicRefCore(
Fortran::lower::PreparedActualArguments &loweredActuals,
const Fortran::evaluate::SpecificIntrinsic *intrinsic,
CallContext &callContext) {
auto &builder = callContext.getBuilder();
const auto &loc = callContext.loc;
assert(intrinsic &&
Fortran::lower::intrinsicRequiresCustomOptionalHandling(
callContext.procRef, *intrinsic, callContext.converter));
auto getArgument = [&](std::size_t i, bool loadArg) -> fir::ExtendedValue {
if (!loweredActuals[i])
return fir::getAbsentIntrinsicArgument();
hlfir::Entity actual = loweredActuals[i]->getActual(loc, builder);
if (loadArg && fir::conformsWithPassByRef(actual.getType())) {
return hlfir::loadTrivialScalar(loc, builder, actual);
}
return Fortran::lower::translateToExtendedValue(loc, builder, actual,
callContext.stmtCtx);
};
auto isPresent = [&](std::size_t i) -> std::optional<mlir::Value> {
if (!loweredActuals[i])
return {builder.createBool(loc, false)};
if (loweredActuals[i]->handleDynamicOptional())
return {loweredActuals[i]->getIsPresent()};
return std::nullopt;
};
assert(callContext.resultType &&
"the elemental intrinsics with custom handling are all functions");
mlir::Type resTy = hlfir::getFortranElementType(*callContext.resultType);
fir::ExtendedValue result = Fortran::lower::lowerCustomIntrinsic(
builder, loc, callContext.getProcedureName(), resTy, isPresent,
getArgument, loweredActuals.size(), callContext.stmtCtx);
return {hlfir::EntityWithAttributes{extendedValueToHlfirEntity(
loc, builder, result, ".tmp.custom_intrinsic_result")}};
}
static std::optional<hlfir::EntityWithAttributes>
genIntrinsicRefCore(Fortran::lower::PreparedActualArguments &loweredActuals,
const Fortran::evaluate::SpecificIntrinsic *intrinsic,
const fir::IntrinsicHandlerEntry &intrinsicEntry,
CallContext &callContext) {
auto &converter = callContext.converter;
if (intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling(
callContext.procRef, *intrinsic, converter))
return genCustomIntrinsicRefCore(loweredActuals, intrinsic, callContext);
llvm::SmallVector<fir::ExtendedValue> operands;
llvm::SmallVector<hlfir::CleanupFunction> cleanupFns;
auto addToCleanups = [&cleanupFns](std::optional<hlfir::CleanupFunction> fn) {
if (fn)
cleanupFns.emplace_back(std::move(*fn));
};
auto &stmtCtx = callContext.stmtCtx;
fir::FirOpBuilder &builder = callContext.getBuilder();
mlir::Location loc = callContext.loc;
const fir::IntrinsicArgumentLoweringRules *argLowering =
intrinsicEntry.getArgumentLoweringRules();
for (auto arg : llvm::enumerate(loweredActuals)) {
if (!arg.value()) {
operands.emplace_back(fir::getAbsentIntrinsicArgument());
continue;
}
if (!argLowering) {
assert(!arg.value()->handleDynamicOptional() &&
"should use genOptionalValue");
hlfir::Entity actual = arg.value()->getActual(loc, builder);
operands.emplace_back(
Fortran::lower::convertToValue(loc, converter, actual, stmtCtx));
continue;
}
auto getActualFortranElementType = [&]() -> mlir::Type {
if (const Fortran::lower::SomeExpr *expr =
callContext.procRef.UnwrapArgExpr(arg.index())) {
mlir::Type type = converter.genType(*expr);
return hlfir::getFortranElementType(type);
}
return builder.getNoneType();
};
fir::ArgLoweringRule argRules =
fir::lowerIntrinsicArgumentAs(*argLowering, arg.index());
if (arg.value()->handleDynamicOptional()) {
mlir::Value isPresent = arg.value()->getIsPresent();
switch (argRules.lowerAs) {
case fir::LowerIntrinsicArgAs::Value: {
auto getActualCb = [&](mlir::Location loc,
fir::FirOpBuilder &builder) -> hlfir::Entity {
return arg.value()->getActual(loc, builder);
};
auto [exv, cleanup] =
genOptionalValue(builder, loc, getActualFortranElementType(),
getActualCb, isPresent);
addToCleanups(std::move(cleanup));
operands.emplace_back(exv);
continue;
}
case fir::LowerIntrinsicArgAs::Addr: {
hlfir::Entity actual = arg.value()->getActual(loc, builder);
auto [exv, cleanup] = genOptionalAddr(builder, loc, actual, isPresent);
addToCleanups(std::move(cleanup));
operands.emplace_back(exv);
continue;
}
case fir::LowerIntrinsicArgAs::Box: {
hlfir::Entity actual = arg.value()->getActual(loc, builder);
auto [exv, cleanup] = genOptionalBox(builder, loc, actual, isPresent);
addToCleanups(std::move(cleanup));
operands.emplace_back(exv);
continue;
}
case fir::LowerIntrinsicArgAs::Inquired: {
hlfir::Entity actual = arg.value()->getActual(loc, builder);
auto [exv, cleanup] =
hlfir::translateToExtendedValue(loc, builder, actual);
addToCleanups(std::move(cleanup));
operands.emplace_back(exv);
continue;
}
}
llvm_unreachable("bad switch");
}
hlfir::Entity actual = arg.value()->getActual(loc, builder);
switch (argRules.lowerAs) {
case fir::LowerIntrinsicArgAs::Value:
operands.emplace_back(
Fortran::lower::convertToValue(loc, converter, actual, stmtCtx));
continue;
case fir::LowerIntrinsicArgAs::Addr:
operands.emplace_back(Fortran::lower::convertToAddress(
loc, converter, actual, stmtCtx, getActualFortranElementType()));
continue;
case fir::LowerIntrinsicArgAs::Box:
operands.emplace_back(Fortran::lower::convertToBox(
loc, converter, actual, stmtCtx, getActualFortranElementType()));
continue;
case fir::LowerIntrinsicArgAs::Inquired:
if (const Fortran::lower::SomeExpr *expr =
callContext.procRef.UnwrapArgExpr(arg.index())) {
if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(
*expr)) {
mlir::Type noneTy = mlir::NoneType::get(builder.getContext());
mlir::Type nullPtrTy = fir::PointerType::get(noneTy);
mlir::Type boxTy = fir::BoxType::get(nullPtrTy);
mlir::Value boxStorage =
fir::factory::genNullBoxStorage(builder, loc, boxTy);
hlfir::EntityWithAttributes nullBoxEntity =
extendedValueToHlfirEntity(loc, builder, boxStorage,
".tmp.null_box");
operands.emplace_back(Fortran::lower::translateToExtendedValue(
loc, builder, nullBoxEntity, stmtCtx));
continue;
}
}
operands.emplace_back(Fortran::lower::translateToExtendedValue(
loc, builder, actual, stmtCtx));
continue;
}
llvm_unreachable("bad switch");
}
std::optional<mlir::Type> scalarResultType;
if (callContext.resultType)
scalarResultType = hlfir::getFortranElementType(*callContext.resultType);
const std::string intrinsicName = callContext.getProcedureName();
auto [resultExv, mustBeFreed] = genIntrinsicCall(
builder, loc, intrinsicEntry, scalarResultType, operands, &converter);
for (const hlfir::CleanupFunction &fn : cleanupFns)
fn();
if (!fir::getBase(resultExv))
return std::nullopt;
hlfir::EntityWithAttributes resultEntity = extendedValueToHlfirEntity(
loc, builder, resultExv, ".tmp.intrinsic_result");
if (resultEntity.isVariable() && intrinsicName != "null") {
assert(!fir::isa_trivial(fir::unwrapRefType(resultEntity.getType())) &&
"expect intrinsic scalar results to not be in memory");
hlfir::AsExprOp asExpr;
if (intrinsicName == "merge")
asExpr = builder.create<hlfir::AsExprOp>(loc, resultEntity);
else
asExpr = builder.create<hlfir::AsExprOp>(
loc, resultEntity, builder.createBool(loc, mustBeFreed));
resultEntity = hlfir::EntityWithAttributes{asExpr.getResult()};
}
return resultEntity;
}
static std::optional<hlfir::EntityWithAttributes> genHLFIRIntrinsicRefCore(
Fortran::lower::PreparedActualArguments &loweredActuals,
const Fortran::evaluate::SpecificIntrinsic *intrinsic,
const fir::IntrinsicHandlerEntry &intrinsicEntry,
CallContext &callContext) {
if (useHlfirIntrinsicOps && callContext.resultType) {
fir::FirOpBuilder &builder = callContext.getBuilder();
mlir::Location loc = callContext.loc;
const std::string intrinsicName = callContext.getProcedureName();
const fir::IntrinsicArgumentLoweringRules *argLowering =
intrinsicEntry.getArgumentLoweringRules();
std::optional<hlfir::EntityWithAttributes> res =
Fortran::lower::lowerHlfirIntrinsic(builder, loc, intrinsicName,
loweredActuals, argLowering,
*callContext.resultType);
if (res)
return res;
}
return genIntrinsicRefCore(loweredActuals, intrinsic, intrinsicEntry,
callContext);
}
namespace {
template <typename ElementalCallBuilderImpl>
class ElementalCallBuilder {
public:
std::optional<hlfir::EntityWithAttributes>
genElementalCall(Fortran::lower::PreparedActualArguments &loweredActuals,
bool isImpure, CallContext &callContext) {
mlir::Location loc = callContext.loc;
fir::FirOpBuilder &builder = callContext.getBuilder();
unsigned numArgs = loweredActuals.size();
mlir::Value shape;
Fortran::lower::PreparedActualArgument *optionalWithShape;
bool mustBeOrdered = isImpure;
for (unsigned i = 0; i < numArgs; ++i) {
auto &preparedActual = loweredActuals[i];
if (preparedActual) {
preparedActual->derefPointersAndAllocatables(loc, builder);
if (!preparedActual->handleDynamicOptional() &&
impl().canLoadActualArgumentBeforeLoop(i))
preparedActual->loadTrivialScalar(loc, builder);
if (!shape && preparedActual->isArray()) {
if (preparedActual->handleDynamicOptional())
optionalWithShape = &*preparedActual;
else
shape = preparedActual->genShape(loc, builder);
}
if (impl().argMayBeModifiedByCall(i))
mustBeOrdered = true;
}
}
if (!shape && optionalWithShape) {
shape = optionalWithShape->genShape(loc, builder);
optionalWithShape->resetOptionalAspect();
}
assert(shape &&
"elemental array calls must have at least one array arguments");
if (mustBeOrdered) {
for (auto &preparedActual : loweredActuals) {
if (preparedActual) {
if (hlfir::AssociateOp associate =
preparedActual->associateIfArrayExpr(loc, builder)) {
fir::FirOpBuilder *bldr = &builder;
callContext.stmtCtx.attachCleanup(
[=]() { bldr->create<hlfir::EndAssociateOp>(loc, associate); });
}
}
}
}
if (!callContext.resultType) {
hlfir::LoopNest loopNest =
hlfir::genLoopNest(loc, builder, shape, !mustBeOrdered);
mlir::ValueRange oneBasedIndices = loopNest.oneBasedIndices;
auto insPt = builder.saveInsertionPoint();
builder.setInsertionPointToStart(loopNest.innerLoop.getBody());
callContext.stmtCtx.pushScope();
for (auto &preparedActual : loweredActuals)
if (preparedActual)
preparedActual->setElementalIndices(oneBasedIndices);
impl().genElementalKernel(loweredActuals, callContext);
callContext.stmtCtx.finalizeAndPop();
builder.restoreInsertionPoint(insPt);
return std::nullopt;
}
mlir::Type elementType =
hlfir::getFortranElementType(*callContext.resultType);
llvm::SmallVector<mlir::Value> typeParams;
if (mlir::isa<fir::CharacterType>(elementType) ||
fir::isRecordWithTypeParameters(elementType)) {
auto charType = mlir::dyn_cast<fir::CharacterType>(elementType);
if (charType && charType.hasConstantLen())
typeParams.push_back(builder.createIntegerConstant(
loc, builder.getIndexType(), charType.getLen()));
else if (charType)
typeParams.push_back(impl().computeDynamicCharacterResultLength(
loweredActuals, callContext));
else
TODO(
loc,
"compute elemental PDT function result length parameters in HLFIR");
}
auto genKernel = [&](mlir::Location l, fir::FirOpBuilder &b,
mlir::ValueRange oneBasedIndices) -> hlfir::Entity {
callContext.stmtCtx.pushScope();
for (auto &preparedActual : loweredActuals)
if (preparedActual)
preparedActual->setElementalIndices(oneBasedIndices);
auto res = *impl().genElementalKernel(loweredActuals, callContext);
callContext.stmtCtx.finalizeAndPop();
return res;
};
mlir::Value polymorphicMold;
if (fir::isPolymorphicType(*callContext.resultType))
polymorphicMold =
impl().getPolymorphicResultMold(loweredActuals, callContext);
mlir::Value elemental =
hlfir::genElementalOp(loc, builder, elementType, shape, typeParams,
genKernel, !mustBeOrdered, polymorphicMold);
bool mustFinalizeExpr = impl().resultMayRequireFinalization(callContext);
fir::FirOpBuilder *bldr = &builder;
callContext.stmtCtx.attachCleanup([=]() {
bldr->create<hlfir::DestroyOp>(loc, elemental, mustFinalizeExpr);
});
return hlfir::EntityWithAttributes{elemental};
}
private:
ElementalCallBuilderImpl &impl() {
return *static_cast<ElementalCallBuilderImpl *>(this);
}
};
class ElementalUserCallBuilder
: public ElementalCallBuilder<ElementalUserCallBuilder> {
public:
ElementalUserCallBuilder(Fortran::lower::CallerInterface &caller,
mlir::FunctionType callSiteType)
: caller{caller}, callSiteType{callSiteType} {}
std::optional<hlfir::Entity>
genElementalKernel(Fortran::lower::PreparedActualArguments &loweredActuals,
CallContext &callContext) {
return genUserCall(loweredActuals, caller, callSiteType, callContext);
}
bool argMayBeModifiedByCall(unsigned argIdx) const {
assert(argIdx < caller.getPassedArguments().size() && "bad argument index");
return caller.getPassedArguments()[argIdx].mayBeModifiedByCall();
}
bool canLoadActualArgumentBeforeLoop(unsigned argIdx) const {
using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
const auto &passedArgs{caller.getPassedArguments()};
assert(argIdx < passedArgs.size() && "bad argument index");
const auto &arg{passedArgs[argIdx]};
return arg.passBy == PassBy::Value ||
arg.passBy == PassBy::BaseAddressValueAttribute;
}
mlir::Value computeDynamicCharacterResultLength(
Fortran::lower::PreparedActualArguments &loweredActuals,
CallContext &callContext) {
fir::FirOpBuilder &builder = callContext.getBuilder();
mlir::Location loc = callContext.loc;
auto &converter = callContext.converter;
mlir::Type idxTy = builder.getIndexType();
llvm::SmallVector<CallCleanUp> callCleanUps;
prepareUserCallArguments(loweredActuals, caller, callSiteType, callContext,
callCleanUps);
callContext.symMap.pushScope();
for (const auto &arg : caller.getPassedArguments()) {
const Fortran::semantics::Symbol *sym = caller.getDummySymbol(arg);
assert(sym && "expect symbol for dummy argument");
auto input = caller.getInput(arg);
fir::ExtendedValue exv = Fortran::lower::translateToExtendedValue(
loc, builder, hlfir::Entity{input}, callContext.stmtCtx);
fir::FortranVariableOpInterface variableIface = hlfir::genDeclare(
loc, builder, exv, "dummy.tmp", fir::FortranVariableFlagsAttr{});
callContext.symMap.addVariableDefinition(*sym, variableIface);
}
auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value {
mlir::Value convertExpr = builder.createConvert(
loc, idxTy,
fir::getBase(converter.genExprValue(expr, callContext.stmtCtx)));
return fir::factory::genMaxWithZero(builder, loc, convertExpr);
};
llvm::SmallVector<mlir::Value> lengths;
caller.walkResultLengths(
[&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) {
assert(!isAssumedSizeExtent && "result cannot be assumed-size");
lengths.emplace_back(lowerSpecExpr(e));
});
callContext.symMap.popScope();
assert(lengths.size() == 1 && "expect 1 length parameter for the result");
return lengths[0];
}
mlir::Value getPolymorphicResultMold(
Fortran::lower::PreparedActualArguments &loweredActuals,
CallContext &callContext) {
fir::emitFatalError(callContext.loc,
"elemental function call with polymorphic result");
return {};
}
bool resultMayRequireFinalization(CallContext &callContext) const {
std::optional<Fortran::evaluate::DynamicType> retTy =
caller.getCallDescription().proc().GetType();
if (!retTy)
return false;
if (retTy->IsPolymorphic() || retTy->IsUnlimitedPolymorphic())
fir::emitFatalError(
callContext.loc,
"elemental function call with [unlimited-]polymorphic result");
if (retTy->category() == Fortran::common::TypeCategory::Derived) {
const Fortran::semantics::DerivedTypeSpec &typeSpec =
retTy->GetDerivedTypeSpec();
return Fortran::semantics::IsFinalizable(typeSpec);
}
return false;
}
private:
Fortran::lower::CallerInterface &caller;
mlir::FunctionType callSiteType;
};
class ElementalIntrinsicCallBuilder
: public ElementalCallBuilder<ElementalIntrinsicCallBuilder> {
public:
ElementalIntrinsicCallBuilder(
const Fortran::evaluate::SpecificIntrinsic *intrinsic,
const fir::IntrinsicHandlerEntry &intrinsicEntry, bool isFunction)
: intrinsic{intrinsic}, intrinsicEntry{intrinsicEntry},
isFunction{isFunction} {}
std::optional<hlfir::Entity>
genElementalKernel(Fortran::lower::PreparedActualArguments &loweredActuals,
CallContext &callContext) {
return genHLFIRIntrinsicRefCore(loweredActuals, intrinsic, intrinsicEntry,
callContext);
}
bool argMayBeModifiedByCall(int) const { return !isFunction; }
bool canLoadActualArgumentBeforeLoop(int) const {
return isFunction;
}
mlir::Value computeDynamicCharacterResultLength(
Fortran::lower::PreparedActualArguments &loweredActuals,
CallContext &callContext) {
if (intrinsic)
if (intrinsic->name == "adjustr" || intrinsic->name == "adjustl" ||
intrinsic->name == "merge")
return loweredActuals[0].value().genCharLength(
callContext.loc, callContext.getBuilder());
TODO(callContext.loc,
"compute elemental character min/max function result length in HLFIR");
}
mlir::Value getPolymorphicResultMold(
Fortran::lower::PreparedActualArguments &loweredActuals,
CallContext &callContext) {
if (!intrinsic)
return {};
if (intrinsic->name == "merge") {
assert(!loweredActuals.empty());
return loweredActuals.front()->getPolymorphicMold(callContext.loc);
}
return {};
}
bool resultMayRequireFinalization(
[[maybe_unused]] CallContext &callContext) const {
return false;
}
private:
const Fortran::evaluate::SpecificIntrinsic *intrinsic;
fir::IntrinsicHandlerEntry intrinsicEntry;
const bool isFunction;
};
}
static std::optional<mlir::Value>
genIsPresentIfArgMaybeAbsent(mlir::Location loc, hlfir::Entity actual,
const Fortran::lower::SomeExpr &expr,
CallContext &callContext,
bool passAsAllocatableOrPointer) {
if (!Fortran::evaluate::MayBePassedAsAbsentOptional(expr))
return std::nullopt;
fir::FirOpBuilder &builder = callContext.getBuilder();
if (!passAsAllocatableOrPointer &&
Fortran::evaluate::IsAllocatableOrPointerObject(expr)) {
mlir::Value addr = genVariableRawAddress(loc, builder, actual);
return builder.genIsNotNullAddr(loc, addr);
}
return builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), actual)
.getResult();
}
static std::optional<hlfir::EntityWithAttributes>
genCustomElementalIntrinsicRef(
const Fortran::evaluate::SpecificIntrinsic *intrinsic,
CallContext &callContext) {
assert(callContext.isElementalProcWithArrayArgs() &&
"Use genCustomIntrinsicRef for scalar calls");
mlir::Location loc = callContext.loc;
auto &converter = callContext.converter;
Fortran::lower::PreparedActualArguments operands;
assert(intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling(
callContext.procRef, *intrinsic, converter));
auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) {
hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR(
loc, converter, expr, callContext.symMap, callContext.stmtCtx);
std::optional<mlir::Value> isPresent =
genIsPresentIfArgMaybeAbsent(loc, actual, expr, callContext,
false);
operands.emplace_back(
Fortran::lower::PreparedActualArgument{actual, isPresent});
};
auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr,
fir::LowerIntrinsicArgAs lowerAs) {
hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR(
loc, converter, expr, callContext.symMap, callContext.stmtCtx);
operands.emplace_back(Fortran::lower::PreparedActualArgument{
actual, std::nullopt});
};
Fortran::lower::prepareCustomIntrinsicArgument(
callContext.procRef, *intrinsic, callContext.resultType,
prepareOptionalArg, prepareOtherArg, converter);
std::optional<fir::IntrinsicHandlerEntry> intrinsicEntry =
fir::lookupIntrinsicHandler(callContext.getBuilder(),
callContext.getProcedureName(),
callContext.resultType);
assert(intrinsicEntry.has_value() &&
"intrinsic with custom handling for OPTIONAL arguments must have "
"lowering entries");
return ElementalIntrinsicCallBuilder{intrinsic, *intrinsicEntry,
true}
.genElementalCall(operands, false, callContext);
}
static std::optional<hlfir::EntityWithAttributes>
genCustomIntrinsicRef(const Fortran::evaluate::SpecificIntrinsic *intrinsic,
CallContext &callContext) {
assert(!callContext.isElementalProcWithArrayArgs() &&
"Needs to be run through ElementalIntrinsicCallBuilder first");
mlir::Location loc = callContext.loc;
fir::FirOpBuilder &builder = callContext.getBuilder();
auto &converter = callContext.converter;
auto &stmtCtx = callContext.stmtCtx;
assert(intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling(
callContext.procRef, *intrinsic, converter));
Fortran::lower::PreparedActualArguments loweredActuals;
auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) {
hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR(
loc, converter, expr, callContext.symMap, callContext.stmtCtx);
mlir::Value isPresent =
genIsPresentIfArgMaybeAbsent(loc, actual, expr, callContext,
false)
.value();
loweredActuals.emplace_back(
Fortran::lower::PreparedActualArgument{actual, {isPresent}});
};
auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr,
fir::LowerIntrinsicArgAs lowerAs) {
auto getActualFortranElementType = [&]() -> mlir::Type {
return hlfir::getFortranElementType(converter.genType(expr));
};
hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR(
loc, converter, expr, callContext.symMap, callContext.stmtCtx);
std::optional<fir::ExtendedValue> exv;
switch (lowerAs) {
case fir::LowerIntrinsicArgAs::Value:
exv = Fortran::lower::convertToValue(loc, converter, actual, stmtCtx);
break;
case fir::LowerIntrinsicArgAs::Addr:
exv = Fortran::lower::convertToAddress(loc, converter, actual, stmtCtx,
getActualFortranElementType());
break;
case fir::LowerIntrinsicArgAs::Box:
exv = Fortran::lower::convertToBox(loc, converter, actual, stmtCtx,
getActualFortranElementType());
break;
case fir::LowerIntrinsicArgAs::Inquired:
exv = Fortran::lower::translateToExtendedValue(loc, builder, actual,
stmtCtx);
break;
}
if (!exv)
llvm_unreachable("bad switch");
actual = extendedValueToHlfirEntity(loc, builder, exv.value(),
"tmp.custom_intrinsic_arg");
loweredActuals.emplace_back(Fortran::lower::PreparedActualArgument{
actual, std::nullopt});
};
Fortran::lower::prepareCustomIntrinsicArgument(
callContext.procRef, *intrinsic, callContext.resultType,
prepareOptionalArg, prepareOtherArg, converter);
return genCustomIntrinsicRefCore(loweredActuals, intrinsic, callContext);
}
static std::optional<hlfir::EntityWithAttributes>
genIntrinsicRef(const Fortran::evaluate::SpecificIntrinsic *intrinsic,
const fir::IntrinsicHandlerEntry &intrinsicEntry,
CallContext &callContext) {
mlir::Location loc = callContext.loc;
Fortran::lower::PreparedActualArguments loweredActuals;
const fir::IntrinsicArgumentLoweringRules *argLowering =
intrinsicEntry.getArgumentLoweringRules();
for (const auto &arg : llvm::enumerate(callContext.procRef.arguments())) {
if (!arg.value()) {
loweredActuals.push_back(std::nullopt);
continue;
}
auto *expr =
Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg.value());
if (!expr) {
const Fortran::evaluate::Symbol *assumedTypeSym =
arg.value()->GetAssumedTypeDummy();
if (!assumedTypeSym)
fir::emitFatalError(loc,
"expected assumed-type symbol as actual argument");
std::optional<fir::FortranVariableOpInterface> var =
callContext.symMap.lookupVariableDefinition(*assumedTypeSym);
if (!var)
fir::emitFatalError(loc, "assumed-type symbol was not lowered");
assert(
(!argLowering ||
!fir::lowerIntrinsicArgumentAs(*argLowering, arg.index())
.handleDynamicOptional) &&
"TYPE(*) are not expected to appear as optional intrinsic arguments");
loweredActuals.push_back(Fortran::lower::PreparedActualArgument{
hlfir::Entity{*var}, std::nullopt});
continue;
}
auto loweredActual = Fortran::lower::convertExprToHLFIR(
loc, callContext.converter, *expr, callContext.symMap,
callContext.stmtCtx);
std::optional<mlir::Value> isPresent;
if (argLowering) {
fir::ArgLoweringRule argRules =
fir::lowerIntrinsicArgumentAs(*argLowering, arg.index());
if (argRules.handleDynamicOptional)
isPresent =
genIsPresentIfArgMaybeAbsent(loc, loweredActual, *expr, callContext,
false);
}
loweredActuals.push_back(
Fortran::lower::PreparedActualArgument{loweredActual, isPresent});
}
if (callContext.isElementalProcWithArrayArgs()) {
const bool isFunction = callContext.resultType.has_value();
return ElementalIntrinsicCallBuilder{intrinsic, intrinsicEntry, isFunction}
.genElementalCall(loweredActuals, !isFunction,
callContext);
}
std::optional<hlfir::EntityWithAttributes> result = genHLFIRIntrinsicRefCore(
loweredActuals, intrinsic, intrinsicEntry, callContext);
if (result && mlir::isa<hlfir::ExprType>(result->getType())) {
fir::FirOpBuilder *bldr = &callContext.getBuilder();
callContext.stmtCtx.attachCleanup(
[=]() { bldr->create<hlfir::DestroyOp>(loc, *result); });
}
return result;
}
static std::optional<hlfir::EntityWithAttributes>
genIntrinsicRef(const Fortran::evaluate::SpecificIntrinsic *intrinsic,
CallContext &callContext) {
mlir::Location loc = callContext.loc;
auto &converter = callContext.converter;
if (intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling(
callContext.procRef, *intrinsic, converter)) {
if (callContext.isElementalProcWithArrayArgs())
return genCustomElementalIntrinsicRef(intrinsic, callContext);
return genCustomIntrinsicRef(intrinsic, callContext);
}
std::optional<fir::IntrinsicHandlerEntry> intrinsicEntry =
fir::lookupIntrinsicHandler(callContext.getBuilder(),
callContext.getProcedureName(),
callContext.resultType);
if (!intrinsicEntry)
fir::crashOnMissingIntrinsic(loc, callContext.getProcedureName());
return genIntrinsicRef(intrinsic, *intrinsicEntry, callContext);
}
static std::optional<hlfir::EntityWithAttributes>
genProcedureRef(CallContext &callContext) {
mlir::Location loc = callContext.loc;
fir::FirOpBuilder &builder = callContext.getBuilder();
if (auto *intrinsic = callContext.procRef.proc().GetSpecificIntrinsic())
return genIntrinsicRef(intrinsic, callContext);
if (Fortran::lower::isIntrinsicModuleProcRef(callContext.procRef) &&
!callContext.isBindcCall())
if (std::optional<fir::IntrinsicHandlerEntry> intrinsicEntry =
fir::lookupIntrinsicHandler(builder, callContext.getProcedureName(),
callContext.resultType))
return genIntrinsicRef(nullptr, *intrinsicEntry, callContext);
if (callContext.isStatementFunctionCall())
return genStmtFunctionRef(loc, callContext.converter, callContext.symMap,
callContext.stmtCtx, callContext.procRef);
Fortran::lower::CallerInterface caller(callContext.procRef,
callContext.converter);
mlir::FunctionType callSiteType = caller.genFunctionType();
const bool isElemental = callContext.isElementalProcWithArrayArgs();
Fortran::lower::PreparedActualArguments loweredActuals;
for (const Fortran::lower::CallInterface<
Fortran::lower::CallerInterface>::PassedEntity &arg :
caller.getPassedArguments())
if (const auto *actual = arg.entity) {
const auto *expr = actual->UnwrapExpr();
if (!expr) {
const Fortran::evaluate::Symbol *assumedTypeSym =
actual->GetAssumedTypeDummy();
if (!assumedTypeSym)
fir::emitFatalError(
loc, "expected assumed-type symbol as actual argument");
std::optional<fir::FortranVariableOpInterface> var =
callContext.symMap.lookupVariableDefinition(*assumedTypeSym);
if (!var)
fir::emitFatalError(loc, "assumed-type symbol was not lowered");
hlfir::Entity actual{*var};
std::optional<mlir::Value> isPresent;
if (arg.isOptional()) {
isPresent =
builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), actual)
.getResult();
}
loweredActuals.push_back(Fortran::lower::PreparedActualArgument{
hlfir::Entity{*var}, isPresent});
continue;
}
if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(
*expr)) {
if ((arg.passBy !=
Fortran::lower::CallerInterface::PassEntityBy::MutableBox) &&
(arg.passBy !=
Fortran::lower::CallerInterface::PassEntityBy::BoxProcRef)) {
assert(
arg.isOptional() &&
"NULL must be passed only to pointer, allocatable, or OPTIONAL");
loweredActuals.emplace_back(std::nullopt);
continue;
}
}
if (isElemental && !arg.hasValueAttribute() &&
Fortran::evaluate::IsVariable(*expr) &&
Fortran::evaluate::HasVectorSubscript(*expr)) {
hlfir::ElementalAddrOp elementalAddr =
Fortran::lower::convertVectorSubscriptedExprToElementalAddr(
loc, callContext.converter, *expr, callContext.symMap,
callContext.stmtCtx);
loweredActuals.emplace_back(
Fortran::lower::PreparedActualArgument{elementalAddr});
continue;
}
auto loweredActual = Fortran::lower::convertExprToHLFIR(
loc, callContext.converter, *expr, callContext.symMap,
callContext.stmtCtx);
std::optional<mlir::Value> isPresent;
if (arg.isOptional())
isPresent = genIsPresentIfArgMaybeAbsent(
loc, loweredActual, *expr, callContext,
arg.passBy ==
Fortran::lower::CallerInterface::PassEntityBy::MutableBox);
loweredActuals.emplace_back(
Fortran::lower::PreparedActualArgument{loweredActual, isPresent});
} else {
loweredActuals.emplace_back(std::nullopt);
}
if (isElemental) {
bool isImpure = false;
if (const Fortran::semantics::Symbol *procSym =
callContext.procRef.proc().GetSymbol())
isImpure = !Fortran::semantics::IsPureProcedure(*procSym);
return ElementalUserCallBuilder{caller, callSiteType}.genElementalCall(
loweredActuals, isImpure, callContext);
}
return genUserCall(loweredActuals, caller, callSiteType, callContext);
}
hlfir::Entity Fortran::lower::PreparedActualArgument::getActual(
mlir::Location loc, fir::FirOpBuilder &builder) const {
if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual)) {
if (oneBasedElementalIndices)
return hlfir::getElementAt(loc, builder, *actualEntity,
*oneBasedElementalIndices);
return *actualEntity;
}
assert(oneBasedElementalIndices && "expect elemental context");
hlfir::ElementalAddrOp elementalAddr =
std::get<hlfir::ElementalAddrOp>(actual);
mlir::IRMapping mapper;
auto alwaysFalse = [](hlfir::ElementalOp) -> bool { return false; };
mlir::Value addr = hlfir::inlineElementalOp(
loc, builder, elementalAddr, *oneBasedElementalIndices, mapper,
alwaysFalse);
assert(elementalAddr.getCleanup().empty() && "no clean-up expected");
elementalAddr.erase();
return hlfir::Entity{addr};
}
bool Fortran::lower::isIntrinsicModuleProcRef(
const Fortran::evaluate::ProcedureRef &procRef) {
const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol();
if (!symbol)
return false;
const Fortran::semantics::Symbol *module =
symbol->GetUltimate().owner().GetSymbol();
return module && module->attrs().test(Fortran::semantics::Attr::INTRINSIC);
}
static bool isInWhereMaskedExpression(fir::FirOpBuilder &builder) {
mlir::Operation *op = builder.getRegion().getParentOp();
return op && op->getParentOfType<hlfir::WhereOp>();
}
std::optional<hlfir::EntityWithAttributes> Fortran::lower::convertCallToHLFIR(
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
const evaluate::ProcedureRef &procRef, std::optional<mlir::Type> resultType,
Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
auto &builder = converter.getFirOpBuilder();
if (resultType && !procRef.IsElemental() &&
isInWhereMaskedExpression(builder) &&
!builder.getRegion().getParentOfType<hlfir::ExactlyOnceOp>()) {
Fortran::lower::StatementContext localStmtCtx;
mlir::Type bogusType = builder.getIndexType();
auto exactlyOnce = builder.create<hlfir::ExactlyOnceOp>(loc, bogusType);
mlir::Block *block = builder.createBlock(&exactlyOnce.getBody());
builder.setInsertionPointToStart(block);
CallContext callContext(procRef, resultType, loc, converter, symMap,
localStmtCtx);
std::optional<hlfir::EntityWithAttributes> res =
genProcedureRef(callContext);
assert(res.has_value() && "must be a function");
auto yield = builder.create<hlfir::YieldOp>(loc, *res);
Fortran::lower::genCleanUpInRegionIfAny(loc, builder, yield.getCleanup(),
localStmtCtx);
builder.setInsertionPointAfter(exactlyOnce);
exactlyOnce->getResult(0).setType(res->getType());
if (hlfir::isFortranValue(exactlyOnce.getResult()))
return hlfir::EntityWithAttributes{exactlyOnce.getResult()};
auto [exv, cleanup] = hlfir::translateToExtendedValue(
loc, builder, hlfir::Entity{exactlyOnce});
assert(!cleanup && "resut is a variable");
return hlfir::genDeclare(loc, builder, exv, ".func.pointer.result",
fir::FortranVariableFlagsAttr{});
}
CallContext callContext(procRef, resultType, loc, converter, symMap, stmtCtx);
return genProcedureRef(callContext);
}
void Fortran::lower::convertUserDefinedAssignmentToHLFIR(
mlir::Location loc, Fortran::lower::AbstractConverter &converter,
const evaluate::ProcedureRef &procRef, hlfir::Entity lhs, hlfir::Entity rhs,
Fortran::lower::SymMap &symMap) {
Fortran::lower::StatementContext definedAssignmentContext;
CallContext callContext(procRef, std::nullopt, loc, converter,
symMap, definedAssignmentContext);
Fortran::lower::CallerInterface caller(procRef, converter);
mlir::FunctionType callSiteType = caller.genFunctionType();
PreparedActualArgument preparedLhs{lhs, std::nullopt};
PreparedActualArgument preparedRhs{rhs, std::nullopt};
PreparedActualArguments loweredActuals{preparedLhs, preparedRhs};
genUserCall(loweredActuals, caller, callSiteType, callContext);
return;
}