#include "flang/Lower/CallInterface.h"
#include "flang/Common/Fortran.h"
#include "flang/Evaluate/fold.h"
#include "flang/Lower/Bridge.h"
#include "flang/Lower/Mangler.h"
#include "flang/Lower/PFTBuilder.h"
#include "flang/Lower/StatementContext.h"
#include "flang/Lower/Support/Utils.h"
#include "flang/Optimizer/Builder/Character.h"
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/FIROpsSupport.h"
#include "flang/Optimizer/Support/InternalNames.h"
#include "flang/Optimizer/Support/Utils.h"
#include "flang/Semantics/symbol.h"
#include "flang/Semantics/tools.h"
#include <optional>
static mlir::FunctionType
getProcedureType(const Fortran::evaluate::characteristics::Procedure &proc,
Fortran::lower::AbstractConverter &converter);
mlir::Type Fortran::lower::getUntypedBoxProcType(mlir::MLIRContext *context) {
llvm::SmallVector<mlir::Type> resultTys;
llvm::SmallVector<mlir::Type> inputTys;
auto untypedFunc = mlir::FunctionType::get(context, inputTys, resultTys);
return fir::BoxProcType::get(context, untypedFunc);
}
static mlir::Type getProcedureDesignatorType(
const Fortran::evaluate::characteristics::Procedure *,
Fortran::lower::AbstractConverter &converter) {
return Fortran::lower::getUntypedBoxProcType(&converter.getMLIRContext());
}
bool Fortran::lower::CallerInterface::hasAlternateReturns() const {
return procRef.hasAlternateReturns();
}
static std::string
getProcMangledName(const Fortran::evaluate::ProcedureDesignator &proc,
Fortran::lower::AbstractConverter &converter) {
if (const Fortran::semantics::Symbol *symbol = proc.GetSymbol())
return converter.mangleName(symbol->GetUltimate());
assert(proc.GetSpecificIntrinsic() &&
"expected intrinsic procedure in designator");
return proc.GetName();
}
std::string Fortran::lower::CallerInterface::getMangledName() const {
return getProcMangledName(procRef.proc(), converter);
}
const Fortran::semantics::Symbol *
Fortran::lower::CallerInterface::getProcedureSymbol() const {
return procRef.proc().GetSymbol();
}
bool Fortran::lower::CallerInterface::isIndirectCall() const {
if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
return Fortran::semantics::IsPointer(*symbol) ||
Fortran::semantics::IsDummy(*symbol);
return false;
}
bool Fortran::lower::CallerInterface::requireDispatchCall() const {
if (const Fortran::semantics::Symbol *sym = procRef.proc().GetSymbol())
if (Fortran::semantics::IsPointer(*sym))
return false;
if (const Fortran::evaluate::Component *component =
procRef.proc().GetComponent()) {
if (Fortran::semantics::IsPolymorphic(component->base().GetLastSymbol()))
return true;
}
std::optional<unsigned> passArg = getPassArgIndex();
if (passArg)
return true;
return false;
}
std::optional<unsigned>
Fortran::lower::CallerInterface::getPassArgIndex() const {
unsigned passArgIdx = 0;
std::optional<unsigned> passArg;
for (const auto &arg : getCallDescription().arguments()) {
if (arg && arg->isPassedObject()) {
passArg = passArgIdx;
break;
}
++passArgIdx;
}
if (!passArg)
return passArg;
if (std::optional<Fortran::lower::CallInterface<
Fortran::lower::CallerInterface>::PassedEntity>
resultArg = getPassedResult()) {
if (resultArg->passBy == PassEntityBy::AddressAndLength)
passArg = *passArg + 2;
else if (resultArg->passBy == PassEntityBy::BaseAddress)
passArg = *passArg + 1;
}
return passArg;
}
mlir::Value Fortran::lower::CallerInterface::getIfPassedArg() const {
if (std::optional<unsigned> passArg = getPassArgIndex()) {
assert(actualInputs.size() > *passArg && actualInputs[*passArg] &&
"passed arg was not set yet");
return actualInputs[*passArg];
}
return {};
}
const Fortran::evaluate::ProcedureDesignator *
Fortran::lower::CallerInterface::getIfIndirectCall() const {
if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
if (Fortran::semantics::IsPointer(*symbol) ||
Fortran::semantics::IsDummy(*symbol))
return &procRef.proc();
return nullptr;
}
static mlir::Location
getProcedureDesignatorLoc(const Fortran::evaluate::ProcedureDesignator &proc,
Fortran::lower::AbstractConverter &converter) {
if (const Fortran::semantics::Symbol *symbol = proc.GetSymbol())
return converter.genLocation(symbol->name());
return converter.getCurrentLocation();
}
mlir::Location Fortran::lower::CallerInterface::getCalleeLocation() const {
return getProcedureDesignatorLoc(procRef.proc(), converter);
}
static Fortran::evaluate::characteristics::DummyDataObject
asImplicitArg(Fortran::evaluate::characteristics::DummyDataObject &&dummy) {
std::optional<Fortran::evaluate::Shape> shape =
dummy.type.attrs().none()
? dummy.type.shape()
: std::make_optional<Fortran::evaluate::Shape>(dummy.type.Rank());
return Fortran::evaluate::characteristics::DummyDataObject(
Fortran::evaluate::characteristics::TypeAndShape(dummy.type.type(),
std::move(shape)));
}
static Fortran::evaluate::characteristics::DummyArgument
asImplicitArg(Fortran::evaluate::characteristics::DummyArgument &&dummy) {
return Fortran::common::visit(
Fortran::common::visitors{
[&](Fortran::evaluate::characteristics::DummyDataObject &obj) {
return Fortran::evaluate::characteristics::DummyArgument(
std::move(dummy.name), asImplicitArg(std::move(obj)));
},
[&](Fortran::evaluate::characteristics::DummyProcedure &proc) {
return Fortran::evaluate::characteristics::DummyArgument(
std::move(dummy.name), std::move(proc));
},
[](Fortran::evaluate::characteristics::AlternateReturn &x) {
return Fortran::evaluate::characteristics::DummyArgument(
std::move(x));
}},
dummy.u);
}
static bool isExternalDefinedInSameCompilationUnit(
const Fortran::evaluate::ProcedureDesignator &proc) {
if (const auto *symbol{proc.GetSymbol()})
return symbol->has<Fortran::semantics::SubprogramDetails>() &&
symbol->owner().IsGlobal();
return false;
}
Fortran::evaluate::characteristics::Procedure
Fortran::lower::CallerInterface::characterize() const {
Fortran::evaluate::FoldingContext &foldingContext =
converter.getFoldingContext();
std::optional<Fortran::evaluate::characteristics::Procedure> characteristic =
Fortran::evaluate::characteristics::Procedure::Characterize(
procRef.proc(), foldingContext, false);
assert(characteristic && "Failed to get characteristic from procRef");
if (!characteristic->HasExplicitInterface() ||
(converter.getLoweringOptions().getLowerToHighLevelFIR() &&
isExternalDefinedInSameCompilationUnit(procRef.proc()) &&
characteristic->CanBeCalledViaImplicitInterface())) {
characteristic->dummyArguments.clear();
for (const std::optional<Fortran::evaluate::ActualArgument> &arg :
procRef.arguments()) {
if (arg.has_value()) {
if (arg.value().isAlternateReturn()) {
characteristic->dummyArguments.emplace_back(
Fortran::evaluate::characteristics::AlternateReturn{});
} else {
const Fortran::lower::SomeExpr *expr = arg.value().UnwrapExpr();
assert(expr && "argument in call with implicit interface cannot be "
"assumed type");
std::optional<Fortran::evaluate::characteristics::DummyArgument>
argCharacteristic =
Fortran::evaluate::characteristics::DummyArgument::FromActual(
"actual", *expr, foldingContext,
true);
assert(argCharacteristic &&
"failed to characterize argument in implicit call");
characteristic->dummyArguments.emplace_back(
asImplicitArg(std::move(*argCharacteristic)));
}
}
}
}
return *characteristic;
}
void Fortran::lower::CallerInterface::placeInput(
const PassedEntity &passedEntity, mlir::Value arg) {
assert(static_cast<int>(actualInputs.size()) > passedEntity.firArgument &&
passedEntity.firArgument >= 0 &&
passedEntity.passBy != CallInterface::PassEntityBy::AddressAndLength &&
"bad arg position");
actualInputs[passedEntity.firArgument] = arg;
}
void Fortran::lower::CallerInterface::placeAddressAndLengthInput(
const PassedEntity &passedEntity, mlir::Value addr, mlir::Value len) {
assert(static_cast<int>(actualInputs.size()) > passedEntity.firArgument &&
static_cast<int>(actualInputs.size()) > passedEntity.firLength &&
passedEntity.firArgument >= 0 && passedEntity.firLength >= 0 &&
passedEntity.passBy == CallInterface::PassEntityBy::AddressAndLength &&
"bad arg position");
actualInputs[passedEntity.firArgument] = addr;
actualInputs[passedEntity.firLength] = len;
}
bool Fortran::lower::CallerInterface::verifyActualInputs() const {
if (getNumFIRArguments() != actualInputs.size())
return false;
for (mlir::Value arg : actualInputs) {
if (!arg)
return false;
}
return true;
}
mlir::Value
Fortran::lower::CallerInterface::getInput(const PassedEntity &passedEntity) {
return actualInputs[passedEntity.firArgument];
}
static void walkLengths(
const Fortran::evaluate::characteristics::TypeAndShape &typeAndShape,
const Fortran::lower::CallerInterface::ExprVisitor &visitor,
Fortran::lower::AbstractConverter &converter) {
Fortran::evaluate::DynamicType dynamicType = typeAndShape.type();
if (dynamicType.category() == Fortran::common::TypeCategory::Character) {
if (std::optional<Fortran::evaluate::ExtentExpr> length =
dynamicType.GetCharLength())
visitor(toEvExpr(*length), false);
} else if (dynamicType.category() == Fortran::common::TypeCategory::Derived &&
!dynamicType.IsUnlimitedPolymorphic()) {
const Fortran::semantics::DerivedTypeSpec &derivedTypeSpec =
dynamicType.GetDerivedTypeSpec();
if (Fortran::semantics::CountLenParameters(derivedTypeSpec) > 0)
TODO(converter.getCurrentLocation(),
"function result with derived type length parameters");
}
}
void Fortran::lower::CallerInterface::walkResultLengths(
const ExprVisitor &visitor) const {
assert(characteristic && "characteristic was not computed");
const Fortran::evaluate::characteristics::FunctionResult &result =
characteristic->functionResult.value();
const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
result.GetTypeAndShape();
assert(typeAndShape && "no result type");
return walkLengths(*typeAndShape, visitor, converter);
}
void Fortran::lower::CallerInterface::walkDummyArgumentLengths(
const PassedEntity &passedEntity, const ExprVisitor &visitor) const {
if (!passedEntity.characteristics)
return;
if (const auto *dummy =
std::get_if<Fortran::evaluate::characteristics::DummyDataObject>(
&passedEntity.characteristics->u))
walkLengths(dummy->type, visitor, converter);
}
static Fortran::evaluate::ExtentExpr
getExtentExpr(const Fortran::semantics::ShapeSpec &shapeSpec) {
if (shapeSpec.ubound().isStar())
return Fortran::evaluate::ExtentExpr{-1};
const auto &ubound = shapeSpec.ubound().GetExplicit();
const auto &lbound = shapeSpec.lbound().GetExplicit();
assert(lbound && ubound && "shape must be explicit");
return Fortran::common::Clone(*ubound) - Fortran::common::Clone(*lbound) +
Fortran::evaluate::ExtentExpr{1};
}
static void
walkExtents(const Fortran::semantics::Symbol &symbol,
const Fortran::lower::CallerInterface::ExprVisitor &visitor) {
if (const auto *objectDetails =
symbol.detailsIf<Fortran::semantics::ObjectEntityDetails>())
if (objectDetails->shape().IsExplicitShape() ||
Fortran::semantics::IsAssumedSizeArray(symbol))
for (const Fortran::semantics::ShapeSpec &shapeSpec :
objectDetails->shape())
visitor(Fortran::evaluate::AsGenericExpr(getExtentExpr(shapeSpec)),
shapeSpec.ubound().isStar());
}
void Fortran::lower::CallerInterface::walkResultExtents(
const ExprVisitor &visitor) const {
const Fortran::semantics::SubprogramDetails *interfaceDetails =
getInterfaceDetails();
if (interfaceDetails) {
walkExtents(interfaceDetails->result(), visitor);
} else {
if (procRef.Rank() != 0)
fir::emitFatalError(
converter.getCurrentLocation(),
"only scalar functions may not have an interface symbol");
}
}
void Fortran::lower::CallerInterface::walkDummyArgumentExtents(
const PassedEntity &passedEntity, const ExprVisitor &visitor) const {
const Fortran::semantics::SubprogramDetails *interfaceDetails =
getInterfaceDetails();
if (!interfaceDetails)
return;
const Fortran::semantics::Symbol *dummy = getDummySymbol(passedEntity);
assert(dummy && "dummy symbol was not set");
walkExtents(*dummy, visitor);
}
bool Fortran::lower::CallerInterface::mustMapInterfaceSymbolsForResult() const {
assert(characteristic && "characteristic was not computed");
const std::optional<Fortran::evaluate::characteristics::FunctionResult>
&result = characteristic->functionResult;
if (!result || result->CanBeReturnedViaImplicitInterface() ||
!getInterfaceDetails() || result->IsProcedurePointer())
return false;
bool allResultSpecExprConstant = true;
auto visitor = [&](const Fortran::lower::SomeExpr &e, bool) {
allResultSpecExprConstant &= Fortran::evaluate::IsConstantExpr(e);
};
walkResultLengths(visitor);
walkResultExtents(visitor);
return !allResultSpecExprConstant;
}
bool Fortran::lower::CallerInterface::mustMapInterfaceSymbolsForDummyArgument(
const PassedEntity &arg) const {
bool allResultSpecExprConstant = true;
auto visitor = [&](const Fortran::lower::SomeExpr &e, bool) {
allResultSpecExprConstant &= Fortran::evaluate::IsConstantExpr(e);
};
walkDummyArgumentLengths(arg, visitor);
walkDummyArgumentExtents(arg, visitor);
return !allResultSpecExprConstant;
}
mlir::Value Fortran::lower::CallerInterface::getArgumentValue(
const semantics::Symbol &sym) const {
mlir::Location loc = converter.getCurrentLocation();
const Fortran::semantics::SubprogramDetails *ifaceDetails =
getInterfaceDetails();
if (!ifaceDetails)
fir::emitFatalError(
loc, "mapping actual and dummy arguments requires an interface");
const std::vector<Fortran::semantics::Symbol *> &dummies =
ifaceDetails->dummyArgs();
auto it = std::find(dummies.begin(), dummies.end(), &sym);
if (it == dummies.end())
fir::emitFatalError(loc, "symbol is not a dummy in this call");
FirValue mlirArgIndex = passedArguments[it - dummies.begin()].firArgument;
return actualInputs[mlirArgIndex];
}
const Fortran::semantics::Symbol *
Fortran::lower::CallerInterface::getDummySymbol(
const PassedEntity &passedEntity) const {
const Fortran::semantics::SubprogramDetails *ifaceDetails =
getInterfaceDetails();
if (!ifaceDetails)
return nullptr;
std::size_t argPosition = 0;
for (const auto &arg : getPassedArguments()) {
if (&arg == &passedEntity)
break;
++argPosition;
}
if (argPosition >= ifaceDetails->dummyArgs().size())
return nullptr;
return ifaceDetails->dummyArgs()[argPosition];
}
mlir::Type Fortran::lower::CallerInterface::getResultStorageType() const {
if (passedResult)
return fir::dyn_cast_ptrEleTy(inputs[passedResult->firArgument].type);
assert(saveResult && !outputs.empty());
return outputs[0].type;
}
mlir::Type Fortran::lower::CallerInterface::getDummyArgumentType(
const PassedEntity &passedEntity) const {
return inputs[passedEntity.firArgument].type;
}
const Fortran::semantics::Symbol &
Fortran::lower::CallerInterface::getResultSymbol() const {
mlir::Location loc = converter.getCurrentLocation();
const Fortran::semantics::SubprogramDetails *ifaceDetails =
getInterfaceDetails();
if (!ifaceDetails)
fir::emitFatalError(
loc, "mapping actual and dummy arguments requires an interface");
return ifaceDetails->result();
}
const Fortran::semantics::SubprogramDetails *
Fortran::lower::CallerInterface::getInterfaceDetails() const {
if (const Fortran::semantics::Symbol *iface =
procRef.proc().GetInterfaceSymbol())
return iface->GetUltimate()
.detailsIf<Fortran::semantics::SubprogramDetails>();
return nullptr;
}
bool Fortran::lower::CalleeInterface::hasAlternateReturns() const {
return !funit.isMainProgram() &&
Fortran::semantics::HasAlternateReturns(funit.getSubprogramSymbol());
}
std::string Fortran::lower::CalleeInterface::getMangledName() const {
if (funit.isMainProgram())
return fir::NameUniquer::doProgramEntry().str();
return converter.mangleName(funit.getSubprogramSymbol());
}
const Fortran::semantics::Symbol *
Fortran::lower::CalleeInterface::getProcedureSymbol() const {
if (funit.isMainProgram())
return funit.getMainProgramSymbol();
return &funit.getSubprogramSymbol();
}
mlir::Location Fortran::lower::CalleeInterface::getCalleeLocation() const {
return converter.genLocation(funit.getStartingSourceLoc());
}
Fortran::evaluate::characteristics::Procedure
Fortran::lower::CalleeInterface::characterize() const {
Fortran::evaluate::FoldingContext &foldingContext =
converter.getFoldingContext();
std::optional<Fortran::evaluate::characteristics::Procedure> characteristic =
Fortran::evaluate::characteristics::Procedure::Characterize(
funit.getSubprogramSymbol(), foldingContext);
assert(characteristic && "Fail to get characteristic from symbol");
return *characteristic;
}
bool Fortran::lower::CalleeInterface::isMainProgram() const {
return funit.isMainProgram();
}
mlir::func::FuncOp
Fortran::lower::CalleeInterface::addEntryBlockAndMapArguments() {
if (!func.getBlocks().empty())
fir::emitFatalError(func.getLoc(),
"cannot process subprogram that was already processed");
func.addEntryBlock();
mapPassedEntities();
return func;
}
bool Fortran::lower::CalleeInterface::hasHostAssociated() const {
return funit.parentHasTupleHostAssoc();
}
mlir::Type Fortran::lower::CalleeInterface::getHostAssociatedTy() const {
assert(hasHostAssociated());
return funit.parentHostAssoc().getArgumentType(converter);
}
mlir::Value Fortran::lower::CalleeInterface::getHostAssociatedTuple() const {
assert(hasHostAssociated() || !funit.getHostAssoc().empty());
return converter.hostAssocTupleValue();
}
static void addSymbolAttribute(mlir::func::FuncOp func,
const Fortran::semantics::Symbol &sym,
mlir::MLIRContext &mlirContext) {
const Fortran::semantics::Symbol &ultimate = sym.GetUltimate();
if (Fortran::semantics::ClassifyProcedure(ultimate) ==
Fortran::semantics::ProcedureDefinitionClass::Internal) {
if (ultimate.owner().kind() ==
Fortran::semantics::Scope::Kind::Subprogram) {
if (const Fortran::semantics::Symbol *hostProcedure =
ultimate.owner().symbol()) {
std::string hostName = Fortran::lower::mangle::mangleName(
*hostProcedure, true);
func->setAttr(
fir::getHostSymbolAttrName(),
mlir::SymbolRefAttr::get(
&mlirContext, mlir::StringAttr::get(&mlirContext, hostName)));
}
} else if (ultimate.owner().kind() ==
Fortran::semantics::Scope::Kind::MainProgram) {
func->setAttr(fir::getHostSymbolAttrName(),
mlir::SymbolRefAttr::get(
&mlirContext,
mlir::StringAttr::get(
&mlirContext, fir::NameUniquer::doProgramEntry())));
}
}
if (IsPureProcedure(sym))
func->setAttr(fir::getFuncPureAttrName(),
mlir::UnitAttr::get(&mlirContext));
if (IsElementalProcedure(sym))
func->setAttr(fir::getFuncElementalAttrName(),
mlir::UnitAttr::get(&mlirContext));
if (sym.attrs().test(Fortran::semantics::Attr::RECURSIVE))
func->setAttr(fir::getFuncRecursiveAttrName(),
mlir::UnitAttr::get(&mlirContext));
if (!Fortran::semantics::IsBindCProcedure(sym))
return;
std::string name =
Fortran::lower::mangle::mangleName(sym, true);
func->setAttr(fir::getSymbolAttrName(),
mlir::StringAttr::get(&mlirContext, name));
}
static void
setCUDAAttributes(mlir::func::FuncOp func,
const Fortran::semantics::Symbol *sym,
std::optional<Fortran::evaluate::characteristics::Procedure>
characteristic) {
if (characteristic && characteristic->cudaSubprogramAttrs) {
func.getOperation()->setAttr(
cuf::getProcAttrName(),
cuf::getProcAttribute(func.getContext(),
*characteristic->cudaSubprogramAttrs));
}
if (sym) {
if (auto details =
sym->GetUltimate()
.detailsIf<Fortran::semantics::SubprogramDetails>()) {
mlir::Type i64Ty = mlir::IntegerType::get(func.getContext(), 64);
if (!details->cudaLaunchBounds().empty()) {
assert(details->cudaLaunchBounds().size() >= 2 &&
"expect at least 2 values");
auto maxTPBAttr =
mlir::IntegerAttr::get(i64Ty, details->cudaLaunchBounds()[0]);
auto minBPMAttr =
mlir::IntegerAttr::get(i64Ty, details->cudaLaunchBounds()[1]);
mlir::IntegerAttr ubAttr;
if (details->cudaLaunchBounds().size() > 2)
ubAttr =
mlir::IntegerAttr::get(i64Ty, details->cudaLaunchBounds()[2]);
func.getOperation()->setAttr(
cuf::getLaunchBoundsAttrName(),
cuf::LaunchBoundsAttr::get(func.getContext(), maxTPBAttr,
minBPMAttr, ubAttr));
}
if (!details->cudaClusterDims().empty()) {
assert(details->cudaClusterDims().size() == 3 && "expect 3 values");
auto xAttr =
mlir::IntegerAttr::get(i64Ty, details->cudaClusterDims()[0]);
auto yAttr =
mlir::IntegerAttr::get(i64Ty, details->cudaClusterDims()[1]);
auto zAttr =
mlir::IntegerAttr::get(i64Ty, details->cudaClusterDims()[2]);
func.getOperation()->setAttr(
cuf::getClusterDimsAttrName(),
cuf::ClusterDimsAttr::get(func.getContext(), xAttr, yAttr, zAttr));
}
}
}
}
template <typename T>
void Fortran::lower::CallInterface<T>::declare() {
if (!side().isMainProgram()) {
characteristic.emplace(side().characterize());
bool isImplicit = characteristic->CanBeCalledViaImplicitInterface();
determineInterface(isImplicit, *characteristic);
}
if (!side().isIndirectCall()) {
std::string name = side().getMangledName();
mlir::ModuleOp module = converter.getModuleOp();
mlir::SymbolTable *symbolTable = converter.getMLIRSymbolTable();
func = fir::FirOpBuilder::getNamedFunction(module, symbolTable, name);
if (!func) {
mlir::Location loc = side().getCalleeLocation();
mlir::FunctionType ty = genFunctionType();
func =
fir::FirOpBuilder::createFunction(loc, module, name, ty, symbolTable);
if (const Fortran::semantics::Symbol *sym = side().getProcedureSymbol()) {
if (side().isMainProgram()) {
func->setAttr(fir::getSymbolAttrName(),
mlir::StringAttr::get(&converter.getMLIRContext(),
sym->name().ToString()));
} else {
addSymbolAttribute(func, *sym, converter.getMLIRContext());
}
}
for (const auto &placeHolder : llvm::enumerate(inputs))
if (!placeHolder.value().attributes.empty())
func.setArgAttrs(placeHolder.index(), placeHolder.value().attributes);
setCUDAAttributes(func, side().getProcedureSymbol(), characteristic);
}
}
}
template <typename T>
void Fortran::lower::CallInterface<T>::mapPassedEntities() {
if constexpr (std::is_same_v<T, Fortran::lower::CalleeInterface>) {
assert(inputs.size() == func.front().getArguments().size() &&
"function previously created with different number of arguments");
for (auto [fst, snd] : llvm::zip(inputs, func.front().getArguments()))
mapBackInputToPassedEntity(fst, snd);
} else {
int firPosition = 0;
for (const FirPlaceHolder &placeHolder : inputs)
mapBackInputToPassedEntity(placeHolder, firPosition++);
}
}
template <typename T>
void Fortran::lower::CallInterface<T>::mapBackInputToPassedEntity(
const FirPlaceHolder &placeHolder, FirValue firValue) {
PassedEntity &passedEntity =
placeHolder.passedEntityPosition == FirPlaceHolder::resultEntityPosition
? passedResult.value()
: passedArguments[placeHolder.passedEntityPosition];
if (placeHolder.property == Property::CharLength)
passedEntity.firLength = firValue;
else
passedEntity.firArgument = firValue;
}
static const Fortran::evaluate::ActualArguments &
getEntityContainer(const Fortran::evaluate::ProcedureRef &proc) {
return proc.arguments();
}
static const std::vector<Fortran::semantics::Symbol *> &
getEntityContainer(Fortran::lower::pft::FunctionLikeUnit &funit) {
return funit.getSubprogramSymbol()
.get<Fortran::semantics::SubprogramDetails>()
.dummyArgs();
}
static const Fortran::evaluate::ActualArgument *getDataObjectEntity(
const std::optional<Fortran::evaluate::ActualArgument> &arg) {
if (arg)
return &*arg;
return nullptr;
}
static const Fortran::semantics::Symbol &
getDataObjectEntity(const Fortran::semantics::Symbol *arg) {
assert(arg && "expect symbol for data object entity");
return *arg;
}
static const Fortran::evaluate::ActualArgument *
getResultEntity(const Fortran::evaluate::ProcedureRef &) {
return nullptr;
}
static const Fortran::semantics::Symbol &
getResultEntity(Fortran::lower::pft::FunctionLikeUnit &funit) {
return funit.getSubprogramSymbol()
.get<Fortran::semantics::SubprogramDetails>()
.result();
}
using FakeEntity = bool;
using FakeEntities = llvm::SmallVector<FakeEntity>;
static FakeEntities
getEntityContainer(const Fortran::evaluate::characteristics::Procedure &proc) {
FakeEntities enities(proc.dummyArguments.size());
return enities;
}
static const FakeEntity &getDataObjectEntity(const FakeEntity &e) { return e; }
static FakeEntity
getResultEntity(const Fortran::evaluate::characteristics::Procedure &proc) {
return false;
}
template <typename T>
class Fortran::lower::CallInterfaceImpl {
using CallInterface = Fortran::lower::CallInterface<T>;
using PassEntityBy = typename CallInterface::PassEntityBy;
using PassedEntity = typename CallInterface::PassedEntity;
using FirValue = typename CallInterface::FirValue;
using FortranEntity = typename CallInterface::FortranEntity;
using FirPlaceHolder = typename CallInterface::FirPlaceHolder;
using Property = typename CallInterface::Property;
using TypeAndShape = Fortran::evaluate::characteristics::TypeAndShape;
using DummyCharacteristics =
Fortran::evaluate::characteristics::DummyArgument;
public:
CallInterfaceImpl(CallInterface &i)
: interface(i), mlirContext{i.converter.getMLIRContext()} {}
void buildImplicitInterface(
const Fortran::evaluate::characteristics::Procedure &procedure) {
if (const std::optional<Fortran::evaluate::characteristics::FunctionResult>
&result = procedure.functionResult)
handleImplicitResult(*result, procedure.IsBindC());
else if (interface.side().hasAlternateReturns())
addFirResult(mlir::IndexType::get(&mlirContext),
FirPlaceHolder::resultEntityPosition, Property::Value);
const auto &argumentEntities =
getEntityContainer(interface.side().getCallDescription());
for (auto pair : llvm::zip(procedure.dummyArguments, argumentEntities)) {
const Fortran::evaluate::characteristics::DummyArgument
&argCharacteristics = std::get<0>(pair);
Fortran::common::visit(
Fortran::common::visitors{
[&](const auto &dummy) {
const auto &entity = getDataObjectEntity(std::get<1>(pair));
handleImplicitDummy(&argCharacteristics, dummy, entity);
},
[&](const Fortran::evaluate::characteristics::AlternateReturn &) {
},
},
argCharacteristics.u);
}
}
void buildExplicitInterface(
const Fortran::evaluate::characteristics::Procedure &procedure) {
bool isBindC = procedure.IsBindC();
if (const std::optional<Fortran::evaluate::characteristics::FunctionResult>
&result = procedure.functionResult) {
if (result->CanBeReturnedViaImplicitInterface())
handleImplicitResult(*result, isBindC);
else
handleExplicitResult(*result);
} else if (interface.side().hasAlternateReturns()) {
addFirResult(mlir::IndexType::get(&mlirContext),
FirPlaceHolder::resultEntityPosition, Property::Value);
}
const auto &argumentEntities =
getEntityContainer(interface.side().getCallDescription());
for (auto pair : llvm::zip(procedure.dummyArguments, argumentEntities)) {
const Fortran::evaluate::characteristics::DummyArgument
&argCharacteristics = std::get<0>(pair);
Fortran::common::visit(
Fortran::common::visitors{
[&](const Fortran::evaluate::characteristics::DummyDataObject
&dummy) {
const auto &entity = getDataObjectEntity(std::get<1>(pair));
if (!isBindC && dummy.CanBePassedViaImplicitInterface())
handleImplicitDummy(&argCharacteristics, dummy, entity);
else
handleExplicitDummy(&argCharacteristics, dummy, entity,
isBindC);
},
[&](const Fortran::evaluate::characteristics::DummyProcedure
&dummy) {
const auto &entity = getDataObjectEntity(std::get<1>(pair));
handleImplicitDummy(&argCharacteristics, dummy, entity);
},
[&](const Fortran::evaluate::characteristics::AlternateReturn &) {
},
},
argCharacteristics.u);
}
}
void appendHostAssocTupleArg(mlir::Type tupTy) {
mlir::MLIRContext *ctxt = tupTy.getContext();
addFirOperand(tupTy, nextPassedArgPosition(), Property::BaseAddress,
{mlir::NamedAttribute{
mlir::StringAttr::get(ctxt, fir::getHostAssocAttrName()),
mlir::UnitAttr::get(ctxt)}});
interface.passedArguments.emplace_back(
PassedEntity{PassEntityBy::BaseAddress, std::nullopt,
interface.side().getHostAssociatedTuple(), emptyValue()});
}
static std::optional<Fortran::evaluate::DynamicType> getResultDynamicType(
const Fortran::evaluate::characteristics::Procedure &procedure) {
if (const std::optional<Fortran::evaluate::characteristics::FunctionResult>
&result = procedure.functionResult)
if (const auto *resultTypeAndShape = result->GetTypeAndShape())
return resultTypeAndShape->type();
return std::nullopt;
}
static bool mustPassLengthWithDummyProcedure(
const Fortran::evaluate::characteristics::Procedure &procedure) {
if (std::optional<Fortran::evaluate::DynamicType> type =
getResultDynamicType(procedure))
return type->category() == Fortran::common::TypeCategory::Character;
return false;
}
private:
void handleImplicitResult(
const Fortran::evaluate::characteristics::FunctionResult &result,
bool isBindC) {
if (auto proc{result.IsProcedurePointer()}) {
mlir::Type mlirType = fir::BoxProcType::get(
&mlirContext, getProcedureType(*proc, interface.converter));
addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
Property::Value);
return;
}
const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
result.GetTypeAndShape();
assert(typeAndShape && "expect type for non proc pointer result");
Fortran::evaluate::DynamicType dynamicType = typeAndShape->type();
if (dynamicType.category() == Fortran::common::TypeCategory::Character) {
if (isBindC) {
mlir::Type mlirType = translateDynamicType(dynamicType);
addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
Property::Value);
} else {
handleImplicitCharacterResult(dynamicType);
}
} else if (dynamicType.category() ==
Fortran::common::TypeCategory::Derived) {
if (!dynamicType.GetDerivedTypeSpec().IsVectorType()) {
setSaveResult();
}
mlir::Type mlirType = translateDynamicType(dynamicType);
addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
Property::Value);
} else {
mlir::Type mlirType =
getConverter().genType(dynamicType.category(), dynamicType.kind());
addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
Property::Value);
}
}
void
handleImplicitCharacterResult(const Fortran::evaluate::DynamicType &type) {
int resultPosition = FirPlaceHolder::resultEntityPosition;
setPassedResult(PassEntityBy::AddressAndLength,
getResultEntity(interface.side().getCallDescription()));
mlir::Type lenTy = mlir::IndexType::get(&mlirContext);
std::optional<std::int64_t> constantLen = type.knownLength();
fir::CharacterType::LenType len =
constantLen ? *constantLen : fir::CharacterType::unknownLen();
mlir::Type charRefTy = fir::ReferenceType::get(
fir::CharacterType::get(&mlirContext, type.kind(), len));
mlir::Type boxCharTy = fir::BoxCharType::get(&mlirContext, type.kind());
addFirOperand(charRefTy, resultPosition, Property::CharAddress);
addFirOperand(lenTy, resultPosition, Property::CharLength);
addFirResult(boxCharTy, resultPosition, Property::BoxChar);
}
llvm::SmallVector<mlir::NamedAttribute>
dummyNameAttr(const FortranEntity &entity) {
if constexpr (std::is_same_v<FortranEntity,
std::optional<Fortran::common::Reference<
const Fortran::semantics::Symbol>>>) {
if (entity.has_value()) {
const Fortran::semantics::Symbol *argument = &*entity.value();
return {mlir::NamedAttribute(
mlir::StringAttr::get(&mlirContext, "fir.bindc_name"),
mlir::StringAttr::get(&mlirContext,
toStringRef(argument->name())))};
}
}
return {};
}
mlir::Type
getRefType(Fortran::evaluate::DynamicType dynamicType,
const Fortran::evaluate::characteristics::DummyDataObject &obj) {
mlir::Type type = translateDynamicType(dynamicType);
if (std::optional<fir::SequenceType::Shape> bounds = getBounds(obj.type))
type = fir::SequenceType::get(*bounds, type);
return fir::ReferenceType::get(type);
}
void handleImplicitDummy(
const DummyCharacteristics *characteristics,
const Fortran::evaluate::characteristics::DummyDataObject &obj,
const FortranEntity &entity) {
Fortran::evaluate::DynamicType dynamicType = obj.type.type();
if constexpr (std::is_same_v<FortranEntity,
const Fortran::evaluate::ActualArgument *>) {
if (entity) {
if (entity->isPercentVal()) {
mlir::Type type = translateDynamicType(dynamicType);
addFirOperand(type, nextPassedArgPosition(), Property::Value,
dummyNameAttr(entity));
addPassedArg(PassEntityBy::Value, entity, characteristics);
return;
}
if (entity->isPercentRef()) {
mlir::Type refType = getRefType(dynamicType, obj);
addFirOperand(refType, nextPassedArgPosition(), Property::BaseAddress,
dummyNameAttr(entity));
addPassedArg(PassEntityBy::BaseAddress, entity, characteristics);
return;
}
}
}
if (dynamicType.category() == Fortran::common::TypeCategory::Character) {
mlir::Type boxCharTy =
fir::BoxCharType::get(&mlirContext, dynamicType.kind());
addFirOperand(boxCharTy, nextPassedArgPosition(), Property::BoxChar,
dummyNameAttr(entity));
addPassedArg(PassEntityBy::BoxChar, entity, characteristics);
} else {
mlir::Type refType = getRefType(dynamicType, obj);
addFirOperand(refType, nextPassedArgPosition(), Property::BaseAddress,
dummyNameAttr(entity));
addPassedArg(PassEntityBy::BaseAddress, entity, characteristics);
}
}
mlir::Type
translateDynamicType(const Fortran::evaluate::DynamicType &dynamicType) {
Fortran::common::TypeCategory cat = dynamicType.category();
if (cat == Fortran::common::TypeCategory::Derived) {
if (dynamicType.IsUnlimitedPolymorphic())
return mlir::NoneType::get(&mlirContext);
return getConverter().genType(dynamicType.GetDerivedTypeSpec());
}
if (cat == Fortran::common::TypeCategory::Character)
if (std::optional<std::int64_t> constantLen =
toInt64(dynamicType.GetCharLength()))
return getConverter().genType(cat, dynamicType.kind(), {*constantLen});
return getConverter().genType(cat, dynamicType.kind());
}
void handleExplicitDummy(
const DummyCharacteristics *characteristics,
const Fortran::evaluate::characteristics::DummyDataObject &obj,
const FortranEntity &entity, bool isBindC) {
using Attrs = Fortran::evaluate::characteristics::DummyDataObject::Attr;
bool isValueAttr = false;
[[maybe_unused]] mlir::Location loc =
interface.converter.getCurrentLocation();
llvm::SmallVector<mlir::NamedAttribute> attrs = dummyNameAttr(entity);
auto addMLIRAttr = [&](llvm::StringRef attr) {
attrs.emplace_back(mlir::StringAttr::get(&mlirContext, attr),
mlir::UnitAttr::get(&mlirContext));
};
if (obj.attrs.test(Attrs::Optional))
addMLIRAttr(fir::getOptionalAttrName());
if (obj.attrs.test(Attrs::Contiguous))
addMLIRAttr(fir::getContiguousAttrName());
if (obj.attrs.test(Attrs::Value))
isValueAttr = true;
if (obj.attrs.test(Attrs::Volatile))
TODO(loc, "VOLATILE in procedure interface");
if (obj.attrs.test(Attrs::Target))
addMLIRAttr(fir::getTargetAttrName());
if (obj.cudaDataAttr)
attrs.emplace_back(
mlir::StringAttr::get(&mlirContext, cuf::getDataAttrName()),
cuf::getDataAttribute(&mlirContext, obj.cudaDataAttr));
using ShapeAttr = Fortran::evaluate::characteristics::TypeAndShape::Attr;
const Fortran::evaluate::characteristics::TypeAndShape::Attrs &shapeAttrs =
obj.type.attrs();
if (shapeAttrs.test(ShapeAttr::Coarray))
TODO(loc, "coarray: dummy argument coarray in procedure interface");
Fortran::evaluate::DynamicType dynamicType = obj.type.type();
mlir::Type type = translateDynamicType(dynamicType);
if (std::optional<fir::SequenceType::Shape> bounds = getBounds(obj.type))
type = fir::SequenceType::get(*bounds, type);
if (obj.attrs.test(Attrs::Allocatable))
type = fir::HeapType::get(type);
if (obj.attrs.test(Attrs::Pointer))
type = fir::PointerType::get(type);
mlir::Type boxType = fir::wrapInClassOrBoxType(
type, obj.type.type().IsPolymorphic(), obj.type.type().IsAssumedType());
if (obj.attrs.test(Attrs::Allocatable) || obj.attrs.test(Attrs::Pointer)) {
mlir::Type boxRefType = fir::ReferenceType::get(boxType);
addFirOperand(boxRefType, nextPassedArgPosition(), Property::MutableBox,
attrs);
addPassedArg(PassEntityBy::MutableBox, entity, characteristics);
} else if (obj.IsPassedByDescriptor(isBindC)) {
if (isValueAttr &&
!getConverter().getLoweringOptions().getLowerToHighLevelFIR())
TODO(loc, "assumed shape dummy argument with VALUE attribute");
addFirOperand(boxType, nextPassedArgPosition(), Property::Box, attrs);
addPassedArg(PassEntityBy::Box, entity, characteristics);
} else if (dynamicType.category() ==
Fortran::common::TypeCategory::Character) {
if (isValueAttr && isBindC) {
mlir::Type charTy =
fir::CharacterType::getSingleton(&mlirContext, dynamicType.kind());
addFirOperand(charTy, nextPassedArgPosition(), Property::Value, attrs);
addPassedArg(PassEntityBy::Value, entity, characteristics);
} else {
mlir::Type boxCharTy =
fir::BoxCharType::get(&mlirContext, dynamicType.kind());
addFirOperand(boxCharTy, nextPassedArgPosition(), Property::BoxChar,
attrs);
addPassedArg(isValueAttr ? PassEntityBy::CharBoxValueAttribute
: PassEntityBy::BoxChar,
entity, characteristics);
}
} else {
mlir::Type passType = fir::ReferenceType::get(type);
PassEntityBy passBy = PassEntityBy::BaseAddress;
Property prop = Property::BaseAddress;
if (isValueAttr) {
bool isBuiltinCptrType = fir::isa_builtin_cptr_type(type);
if (isBindC || (!mlir::isa<fir::SequenceType>(type) &&
!obj.attrs.test(Attrs::Optional) &&
(dynamicType.category() !=
Fortran::common::TypeCategory::Derived ||
isBuiltinCptrType))) {
passBy = PassEntityBy::Value;
prop = Property::Value;
if (isBuiltinCptrType) {
auto recTy = mlir::dyn_cast<fir::RecordType>(type);
mlir::Type fieldTy = recTy.getTypeList()[0].second;
passType = fir::ReferenceType::get(fieldTy);
} else {
passType = type;
}
} else {
passBy = PassEntityBy::BaseAddressValueAttribute;
}
}
addFirOperand(passType, nextPassedArgPosition(), prop, attrs);
addPassedArg(passBy, entity, characteristics);
}
}
void handleImplicitDummy(
const DummyCharacteristics *characteristics,
const Fortran::evaluate::characteristics::DummyProcedure &proc,
const FortranEntity &entity) {
if (!interface.converter.getLoweringOptions().getLowerToHighLevelFIR() &&
proc.attrs.test(
Fortran::evaluate::characteristics::DummyProcedure::Attr::Pointer))
TODO(interface.converter.getCurrentLocation(),
"procedure pointer arguments");
const Fortran::evaluate::characteristics::Procedure &procedure =
proc.procedure.value();
mlir::Type funcType =
getProcedureDesignatorType(&procedure, interface.converter);
if (proc.attrs.test(Fortran::evaluate::characteristics::DummyProcedure::
Attr::Pointer)) {
funcType = fir::ReferenceType::get(funcType);
addFirOperand(funcType, nextPassedArgPosition(), Property::BoxProcRef);
addPassedArg(PassEntityBy::BoxProcRef, entity, characteristics);
return;
}
std::optional<Fortran::evaluate::DynamicType> resultTy =
getResultDynamicType(procedure);
if (resultTy && mustPassLengthWithDummyProcedure(procedure)) {
mlir::Type tupleType =
fir::factory::getCharacterProcedureTupleType(funcType);
llvm::StringRef charProcAttr = fir::getCharacterProcedureDummyAttrName();
addFirOperand(tupleType, nextPassedArgPosition(), Property::CharProcTuple,
{mlir::NamedAttribute{
mlir::StringAttr::get(&mlirContext, charProcAttr),
mlir::UnitAttr::get(&mlirContext)}});
addPassedArg(PassEntityBy::CharProcTuple, entity, characteristics);
return;
}
addFirOperand(funcType, nextPassedArgPosition(), Property::BaseAddress);
addPassedArg(PassEntityBy::BaseAddress, entity, characteristics);
}
void handleExplicitResult(
const Fortran::evaluate::characteristics::FunctionResult &result) {
using Attr = Fortran::evaluate::characteristics::FunctionResult::Attr;
mlir::Type mlirType;
if (auto proc{result.IsProcedurePointer()}) {
mlirType = fir::BoxProcType::get(
&mlirContext, getProcedureType(*proc, interface.converter));
addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
Property::Value);
return;
}
const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
result.GetTypeAndShape();
assert(typeAndShape && "expect type for non proc pointer result");
mlirType = translateDynamicType(typeAndShape->type());
const auto *resTypeAndShape{result.GetTypeAndShape()};
bool resIsPolymorphic =
resTypeAndShape && resTypeAndShape->type().IsPolymorphic();
bool resIsAssumedType =
resTypeAndShape && resTypeAndShape->type().IsAssumedType();
if (std::optional<fir::SequenceType::Shape> bounds =
getBounds(*typeAndShape))
mlirType = fir::SequenceType::get(*bounds, mlirType);
if (result.attrs.test(Attr::Allocatable))
mlirType = fir::wrapInClassOrBoxType(fir::HeapType::get(mlirType),
resIsPolymorphic, resIsAssumedType);
if (result.attrs.test(Attr::Pointer))
mlirType = fir::wrapInClassOrBoxType(fir::PointerType::get(mlirType),
resIsPolymorphic, resIsAssumedType);
if (fir::isa_char(mlirType)) {
handleImplicitCharacterResult(typeAndShape->type());
return;
}
addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
Property::Value);
setSaveResult();
}
std::optional<fir::SequenceType::Shape> getBounds(
const Fortran::evaluate::characteristics::TypeAndShape &typeAndShape) {
if (typeAndShape.shape() && typeAndShape.shape()->empty())
return std::nullopt;
fir::SequenceType::Shape bounds;
if (typeAndShape.shape())
for (const std::optional<Fortran::evaluate::ExtentExpr> &extent :
*typeAndShape.shape()) {
fir::SequenceType::Extent bound = fir::SequenceType::getUnknownExtent();
if (std::optional<std::int64_t> i = toInt64(extent))
bound = *i;
bounds.emplace_back(bound);
}
return bounds;
}
std::optional<std::int64_t>
toInt64(std::optional<
Fortran::evaluate::Expr<Fortran::evaluate::SubscriptInteger>>
expr) {
if (expr)
return Fortran::evaluate::ToInt64(Fortran::evaluate::Fold(
getConverter().getFoldingContext(), toEvExpr(*expr)));
return std::nullopt;
}
void addFirOperand(
mlir::Type type, int entityPosition, Property p,
llvm::ArrayRef<mlir::NamedAttribute> attributes = std::nullopt) {
interface.inputs.emplace_back(
FirPlaceHolder{type, entityPosition, p, attributes});
}
void
addFirResult(mlir::Type type, int entityPosition, Property p,
llvm::ArrayRef<mlir::NamedAttribute> attributes = std::nullopt) {
interface.outputs.emplace_back(
FirPlaceHolder{type, entityPosition, p, attributes});
}
void addPassedArg(PassEntityBy p, FortranEntity entity,
const DummyCharacteristics *characteristics) {
interface.passedArguments.emplace_back(
PassedEntity{p, entity, emptyValue(), emptyValue(), characteristics});
}
void setPassedResult(PassEntityBy p, FortranEntity entity) {
interface.passedResult =
PassedEntity{p, entity, emptyValue(), emptyValue()};
}
void setSaveResult() { interface.saveResult = true; }
int nextPassedArgPosition() { return interface.passedArguments.size(); }
static FirValue emptyValue() {
if constexpr (std::is_same_v<Fortran::lower::CalleeInterface, T>) {
return {};
} else {
return -1;
}
}
Fortran::lower::AbstractConverter &getConverter() {
return interface.converter;
}
CallInterface &interface;
mlir::MLIRContext &mlirContext;
};
template <typename T>
bool Fortran::lower::CallInterface<T>::PassedEntity::isOptional() const {
if (!characteristics)
return false;
return characteristics->IsOptional();
}
template <typename T>
bool Fortran::lower::CallInterface<T>::PassedEntity::mayBeModifiedByCall()
const {
if (!characteristics)
return true;
if (characteristics->GetIntent() == Fortran::common::Intent::In)
return false;
return !hasValueAttribute();
}
template <typename T>
bool Fortran::lower::CallInterface<T>::PassedEntity::mayBeReadByCall() const {
if (!characteristics)
return true;
return characteristics->GetIntent() != Fortran::common::Intent::Out;
}
template <typename T>
bool Fortran::lower::CallInterface<T>::PassedEntity::testTKR(
Fortran::common::IgnoreTKR flag) const {
if (!characteristics)
return false;
const auto *dummy =
std::get_if<Fortran::evaluate::characteristics::DummyDataObject>(
&characteristics->u);
if (!dummy)
return false;
return dummy->ignoreTKR.test(flag);
}
template <typename T>
bool Fortran::lower::CallInterface<T>::PassedEntity::isIntentOut() const {
if (!characteristics)
return true;
return characteristics->GetIntent() == Fortran::common::Intent::Out;
}
template <typename T>
bool Fortran::lower::CallInterface<T>::PassedEntity::mustBeMadeContiguous()
const {
if (!characteristics)
return true;
const auto *dummy =
std::get_if<Fortran::evaluate::characteristics::DummyDataObject>(
&characteristics->u);
if (!dummy)
return false;
const auto &shapeAttrs = dummy->type.attrs();
using ShapeAttrs = Fortran::evaluate::characteristics::TypeAndShape::Attr;
if (shapeAttrs.test(ShapeAttrs::AssumedRank) ||
shapeAttrs.test(ShapeAttrs::AssumedShape))
return dummy->attrs.test(
Fortran::evaluate::characteristics::DummyDataObject::Attr::Contiguous);
if (shapeAttrs.test(ShapeAttrs::DeferredShape))
return false;
return dummy->type.Rank() > 0;
}
template <typename T>
bool Fortran::lower::CallInterface<T>::PassedEntity::hasValueAttribute() const {
if (!characteristics)
return false;
const auto *dummy =
std::get_if<Fortran::evaluate::characteristics::DummyDataObject>(
&characteristics->u);
return dummy &&
dummy->attrs.test(
Fortran::evaluate::characteristics::DummyDataObject::Attr::Value);
}
template <typename T>
bool Fortran::lower::CallInterface<T>::PassedEntity::hasAllocatableAttribute()
const {
if (!characteristics)
return false;
const auto *dummy =
std::get_if<Fortran::evaluate::characteristics::DummyDataObject>(
&characteristics->u);
using Attrs = Fortran::evaluate::characteristics::DummyDataObject::Attr;
return dummy && dummy->attrs.test(Attrs::Allocatable);
}
template <typename T>
bool Fortran::lower::CallInterface<
T>::PassedEntity::mayRequireIntentoutFinalization() const {
if (!characteristics)
return true;
if (!isIntentOut())
return false;
const auto *dummy =
std::get_if<Fortran::evaluate::characteristics::DummyDataObject>(
&characteristics->u);
if (!dummy)
return true;
using Attrs = Fortran::evaluate::characteristics::DummyDataObject::Attr;
if (dummy->attrs.test(Attrs::Allocatable) ||
dummy->attrs.test(Attrs::Pointer))
return false;
const Fortran::evaluate::DynamicType &type = dummy->type.type();
if (type.IsPolymorphic() || type.IsUnlimitedPolymorphic())
return true;
const Fortran::semantics::DerivedTypeSpec *derived =
Fortran::evaluate::GetDerivedTypeSpec(type);
if (!derived)
return false;
return Fortran::semantics::IsFinalizable(*derived);
}
template <typename T>
bool Fortran::lower::CallInterface<
T>::PassedEntity::isSequenceAssociatedDescriptor() const {
if (!characteristics || passBy != PassEntityBy::Box)
return false;
const auto *dummy =
std::get_if<Fortran::evaluate::characteristics::DummyDataObject>(
&characteristics->u);
return dummy && dummy->type.CanBeSequenceAssociated();
}
template <typename T>
void Fortran::lower::CallInterface<T>::determineInterface(
bool isImplicit,
const Fortran::evaluate::characteristics::Procedure &procedure) {
CallInterfaceImpl<T> impl(*this);
if (isImplicit)
impl.buildImplicitInterface(procedure);
else
impl.buildExplicitInterface(procedure);
if constexpr (std::is_same_v<T, Fortran::lower::CalleeInterface>) {
if (side().hasHostAssociated())
impl.appendHostAssocTupleArg(side().getHostAssociatedTy());
}
}
template <typename T>
mlir::FunctionType Fortran::lower::CallInterface<T>::genFunctionType() {
llvm::SmallVector<mlir::Type> returnTys;
llvm::SmallVector<mlir::Type> inputTys;
for (const FirPlaceHolder &placeHolder : outputs)
returnTys.emplace_back(placeHolder.type);
for (const FirPlaceHolder &placeHolder : inputs)
inputTys.emplace_back(placeHolder.type);
return mlir::FunctionType::get(&converter.getMLIRContext(), inputTys,
returnTys);
}
template <typename T>
llvm::SmallVector<mlir::Type>
Fortran::lower::CallInterface<T>::getResultType() const {
llvm::SmallVector<mlir::Type> types;
for (const FirPlaceHolder &out : outputs)
types.emplace_back(out.type);
return types;
}
template class Fortran::lower::CallInterface<Fortran::lower::CalleeInterface>;
template class Fortran::lower::CallInterface<Fortran::lower::CallerInterface>;
class SignatureBuilder;
template <>
struct Fortran::lower::PassedEntityTypes<SignatureBuilder> {
using FortranEntity = FakeEntity;
using FirValue = int;
};
class SignatureBuilder
: public Fortran::lower::CallInterface<SignatureBuilder> {
public:
SignatureBuilder(const Fortran::evaluate::characteristics::Procedure &p,
Fortran::lower::AbstractConverter &c, bool forceImplicit)
: CallInterface{c}, proc{p} {
bool isImplicit = forceImplicit || proc.CanBeCalledViaImplicitInterface();
determineInterface(isImplicit, proc);
}
SignatureBuilder(const Fortran::evaluate::ProcedureDesignator &procDes,
Fortran::lower::AbstractConverter &c)
: CallInterface{c}, procDesignator{&procDes},
proc{Fortran::evaluate::characteristics::Procedure::Characterize(
procDes, converter.getFoldingContext(), false)
.value()} {}
bool hasAlternateReturns() const {
for (const Fortran::evaluate::characteristics::DummyArgument &dummy :
proc.dummyArguments)
if (std::holds_alternative<
Fortran::evaluate::characteristics::AlternateReturn>(dummy.u))
return true;
return false;
};
std::string getMangledName() const {
if (procDesignator)
return getProcMangledName(*procDesignator, converter);
fir::emitFatalError(
converter.getCurrentLocation(),
"should not query name when only building function type");
}
mlir::Location getCalleeLocation() const {
if (procDesignator)
return getProcedureDesignatorLoc(*procDesignator, converter);
return converter.getCurrentLocation();
}
const Fortran::semantics::Symbol *getProcedureSymbol() const {
if (procDesignator)
return procDesignator->GetSymbol();
return nullptr;
};
Fortran::evaluate::characteristics::Procedure characterize() const {
return proc;
}
static constexpr bool isMainProgram() { return false; }
const Fortran::evaluate::characteristics::Procedure &
getCallDescription() const {
return proc;
}
static constexpr bool isIndirectCall() { return false; }
mlir::FunctionType getFunctionType() {
if (interfaceDetermined)
fir::emitFatalError(converter.getCurrentLocation(),
"SignatureBuilder should only be used once");
bool forceImplicit =
procDesignator && procDesignator->GetSpecificIntrinsic();
bool isImplicit = forceImplicit || proc.CanBeCalledViaImplicitInterface();
determineInterface(isImplicit, proc);
interfaceDetermined = true;
return genFunctionType();
}
mlir::func::FuncOp getOrCreateFuncOp() {
if (interfaceDetermined)
fir::emitFatalError(converter.getCurrentLocation(),
"SignatureBuilder should only be used once");
declare();
interfaceDetermined = true;
return getFuncOp();
}
static constexpr bool hasHostAssociated() { return false; }
mlir::Type getHostAssociatedTy() const {
llvm_unreachable("getting host associated type in SignatureBuilder");
}
private:
const Fortran::evaluate::ProcedureDesignator *procDesignator = nullptr;
Fortran::evaluate::characteristics::Procedure proc;
bool interfaceDetermined = false;
};
mlir::FunctionType Fortran::lower::translateSignature(
const Fortran::evaluate::ProcedureDesignator &proc,
Fortran::lower::AbstractConverter &converter) {
return SignatureBuilder{proc, converter}.getFunctionType();
}
mlir::func::FuncOp Fortran::lower::getOrDeclareFunction(
const Fortran::evaluate::ProcedureDesignator &proc,
Fortran::lower::AbstractConverter &converter) {
mlir::ModuleOp module = converter.getModuleOp();
std::string name = getProcMangledName(proc, converter);
mlir::func::FuncOp func = fir::FirOpBuilder::getNamedFunction(
module, converter.getMLIRSymbolTable(), name);
if (func)
return func;
return SignatureBuilder{proc, converter}.getOrCreateFuncOp();
}
static bool mustPassLengthWithDummyProcedure(
const std::optional<Fortran::evaluate::characteristics::Procedure>
&characteristics) {
return characteristics &&
Fortran::lower::CallInterfaceImpl<SignatureBuilder>::
mustPassLengthWithDummyProcedure(*characteristics);
}
bool Fortran::lower::mustPassLengthWithDummyProcedure(
const Fortran::evaluate::ProcedureDesignator &procedure,
Fortran::lower::AbstractConverter &converter) {
std::optional<Fortran::evaluate::characteristics::Procedure> characteristics =
Fortran::evaluate::characteristics::Procedure::Characterize(
procedure, converter.getFoldingContext(), false);
return ::mustPassLengthWithDummyProcedure(characteristics);
}
mlir::Type Fortran::lower::getDummyProcedureType(
const Fortran::semantics::Symbol &dummyProc,
Fortran::lower::AbstractConverter &converter) {
std::optional<Fortran::evaluate::characteristics::Procedure> iface =
Fortran::evaluate::characteristics::Procedure::Characterize(
dummyProc, converter.getFoldingContext());
mlir::Type procType = getProcedureDesignatorType(
iface.has_value() ? &*iface : nullptr, converter);
if (::mustPassLengthWithDummyProcedure(iface))
return fir::factory::getCharacterProcedureTupleType(procType);
return procType;
}
bool Fortran::lower::isCPtrArgByValueType(mlir::Type ty) {
return mlir::isa<fir::ReferenceType>(ty) &&
fir::isa_integer(fir::unwrapRefType(ty));
}
static mlir::FunctionType
getProcedureType(const Fortran::evaluate::characteristics::Procedure &proc,
Fortran::lower::AbstractConverter &converter) {
return SignatureBuilder{proc, converter, false}.genFunctionType();
}