|  | //===- ConvertProcedureDesignator.cpp -- Procedure Designator ---*- C++ -*-===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "flang/Lower/ConvertProcedureDesignator.h" | 
|  | #include "flang/Evaluate/intrinsics.h" | 
|  | #include "flang/Lower/AbstractConverter.h" | 
|  | #include "flang/Lower/CallInterface.h" | 
|  | #include "flang/Lower/ConvertCall.h" | 
|  | #include "flang/Lower/ConvertExprToHLFIR.h" | 
|  | #include "flang/Lower/ConvertVariable.h" | 
|  | #include "flang/Lower/Support/Utils.h" | 
|  | #include "flang/Lower/SymbolMap.h" | 
|  | #include "flang/Optimizer/Builder/Character.h" | 
|  | #include "flang/Optimizer/Builder/IntrinsicCall.h" | 
|  | #include "flang/Optimizer/Builder/Todo.h" | 
|  | #include "flang/Optimizer/Dialect/FIROps.h" | 
|  | #include "flang/Optimizer/HLFIR/HLFIROps.h" | 
|  |  | 
|  | static bool areAllSymbolsInExprMapped(const Fortran::evaluate::ExtentExpr &expr, | 
|  | Fortran::lower::SymMap &symMap) { | 
|  | for (const auto &sym : Fortran::evaluate::CollectSymbols(expr)) | 
|  | if (!symMap.lookupSymbol(sym)) | 
|  | return false; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | fir::ExtendedValue Fortran::lower::convertProcedureDesignator( | 
|  | mlir::Location loc, Fortran::lower::AbstractConverter &converter, | 
|  | const Fortran::evaluate::ProcedureDesignator &proc, | 
|  | Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { | 
|  | fir::FirOpBuilder &builder = converter.getFirOpBuilder(); | 
|  |  | 
|  | if (const Fortran::evaluate::SpecificIntrinsic *intrinsic = | 
|  | proc.GetSpecificIntrinsic()) { | 
|  | mlir::FunctionType signature = | 
|  | Fortran::lower::translateSignature(proc, converter); | 
|  | // Intrinsic lowering is based on the generic name, so retrieve it here in | 
|  | // case it is different from the specific name. The type of the specific | 
|  | // intrinsic is retained in the signature. | 
|  | std::string genericName = | 
|  | converter.getFoldingContext().intrinsics().GetGenericIntrinsicName( | 
|  | intrinsic->name); | 
|  | mlir::SymbolRefAttr symbolRefAttr = | 
|  | fir::getUnrestrictedIntrinsicSymbolRefAttr(builder, loc, genericName, | 
|  | signature); | 
|  | mlir::Value funcPtr = | 
|  | builder.create<fir::AddrOfOp>(loc, signature, symbolRefAttr); | 
|  | return funcPtr; | 
|  | } | 
|  | const Fortran::semantics::Symbol *symbol = proc.GetSymbol(); | 
|  | assert(symbol && "expected symbol in ProcedureDesignator"); | 
|  | mlir::Value funcPtr; | 
|  | mlir::Value funcPtrResultLength; | 
|  | if (Fortran::semantics::IsDummy(*symbol)) { | 
|  | Fortran::lower::SymbolBox val = symMap.lookupSymbol(*symbol); | 
|  | assert(val && "Dummy procedure not in symbol map"); | 
|  | funcPtr = val.getAddr(); | 
|  | if (fir::isCharacterProcedureTuple(funcPtr.getType(), | 
|  | /*acceptRawFunc=*/false)) | 
|  | std::tie(funcPtr, funcPtrResultLength) = | 
|  | fir::factory::extractCharacterProcedureTuple(builder, loc, funcPtr); | 
|  | } else { | 
|  | mlir::func::FuncOp func = | 
|  | Fortran::lower::getOrDeclareFunction(proc, converter); | 
|  | mlir::SymbolRefAttr nameAttr = builder.getSymbolRefAttr(func.getSymName()); | 
|  | funcPtr = | 
|  | builder.create<fir::AddrOfOp>(loc, func.getFunctionType(), nameAttr); | 
|  | } | 
|  | if (Fortran::lower::mustPassLengthWithDummyProcedure(proc, converter)) { | 
|  | // The result length, if available here, must be propagated along the | 
|  | // procedure address so that call sites where the result length is assumed | 
|  | // can retrieve the length. | 
|  | Fortran::evaluate::DynamicType resultType = proc.GetType().value(); | 
|  | if (const auto &lengthExpr = resultType.GetCharLength()) { | 
|  | // The length expression may refer to dummy argument symbols that are | 
|  | // meaningless without any actual arguments. Leave the length as | 
|  | // unknown in that case, it be resolved on the call site | 
|  | // with the actual arguments. | 
|  | if (areAllSymbolsInExprMapped(*lengthExpr, symMap)) { | 
|  | mlir::Value rawLen = fir::getBase( | 
|  | converter.genExprValue(toEvExpr(*lengthExpr), stmtCtx)); | 
|  | // F2018 7.4.4.2 point 5. | 
|  | funcPtrResultLength = | 
|  | fir::factory::genMaxWithZero(builder, loc, rawLen); | 
|  | } | 
|  | } | 
|  | if (!funcPtrResultLength) | 
|  | funcPtrResultLength = builder.createIntegerConstant( | 
|  | loc, builder.getCharacterLengthType(), -1); | 
|  | return fir::CharBoxValue{funcPtr, funcPtrResultLength}; | 
|  | } | 
|  | return funcPtr; | 
|  | } | 
|  |  | 
|  | static hlfir::EntityWithAttributes designateProcedurePointerComponent( | 
|  | mlir::Location loc, Fortran::lower::AbstractConverter &converter, | 
|  | const Fortran::evaluate::Symbol &procComponentSym, mlir::Value base, | 
|  | Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { | 
|  | fir::FirOpBuilder &builder = converter.getFirOpBuilder(); | 
|  | fir::FortranVariableFlagsAttr attributes = | 
|  | Fortran::lower::translateSymbolAttributes(builder.getContext(), | 
|  | procComponentSym); | 
|  | /// Passed argument may be a descriptor. This is a scalar reference, so the | 
|  | /// base address can be directly addressed. | 
|  | if (mlir::isa<fir::BaseBoxType>(base.getType())) | 
|  | base = builder.create<fir::BoxAddrOp>(loc, base); | 
|  | std::string fieldName = converter.getRecordTypeFieldName(procComponentSym); | 
|  | auto recordType = | 
|  | mlir::cast<fir::RecordType>(hlfir::getFortranElementType(base.getType())); | 
|  | mlir::Type fieldType = recordType.getType(fieldName); | 
|  | // Note: semantics turns x%p() into x%t%p() when the procedure pointer | 
|  | // component is part of parent component t. | 
|  | if (!fieldType) | 
|  | TODO(loc, "passing type bound procedure (extension)"); | 
|  | mlir::Type designatorType = fir::ReferenceType::get(fieldType); | 
|  | mlir::Value compRef = builder.create<hlfir::DesignateOp>( | 
|  | loc, designatorType, base, fieldName, | 
|  | /*compShape=*/mlir::Value{}, hlfir::DesignateOp::Subscripts{}, | 
|  | /*substring=*/mlir::ValueRange{}, | 
|  | /*complexPart=*/std::nullopt, | 
|  | /*shape=*/mlir::Value{}, /*typeParams=*/mlir::ValueRange{}, attributes); | 
|  | return hlfir::EntityWithAttributes{compRef}; | 
|  | } | 
|  |  | 
|  | static hlfir::EntityWithAttributes convertProcedurePointerComponent( | 
|  | mlir::Location loc, Fortran::lower::AbstractConverter &converter, | 
|  | const Fortran::evaluate::Component &procComponent, | 
|  | Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { | 
|  | fir::ExtendedValue baseExv = Fortran::lower::convertDataRefToValue( | 
|  | loc, converter, procComponent.base(), symMap, stmtCtx); | 
|  | mlir::Value base = fir::getBase(baseExv); | 
|  | const Fortran::semantics::Symbol &procComponentSym = | 
|  | procComponent.GetLastSymbol(); | 
|  | return designateProcedurePointerComponent(loc, converter, procComponentSym, | 
|  | base, symMap, stmtCtx); | 
|  | } | 
|  |  | 
|  | hlfir::EntityWithAttributes Fortran::lower::convertProcedureDesignatorToHLFIR( | 
|  | mlir::Location loc, Fortran::lower::AbstractConverter &converter, | 
|  | const Fortran::evaluate::ProcedureDesignator &proc, | 
|  | Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { | 
|  | const auto *sym = proc.GetSymbol(); | 
|  | if (sym) { | 
|  | if (sym->GetUltimate().attrs().test(Fortran::semantics::Attr::INTRINSIC)) | 
|  | TODO(loc, "Procedure pointer with intrinsic target."); | 
|  | if (std::optional<fir::FortranVariableOpInterface> varDef = | 
|  | symMap.lookupVariableDefinition(*sym)) | 
|  | return *varDef; | 
|  | } | 
|  |  | 
|  | if (const Fortran::evaluate::Component *procComponent = proc.GetComponent()) | 
|  | return convertProcedurePointerComponent(loc, converter, *procComponent, | 
|  | symMap, stmtCtx); | 
|  |  | 
|  | fir::ExtendedValue procExv = | 
|  | convertProcedureDesignator(loc, converter, proc, symMap, stmtCtx); | 
|  | // Directly package the procedure address as a fir.boxproc or | 
|  | // tuple<fir.boxbroc, len> so that it can be returned as a single mlir::Value. | 
|  | fir::FirOpBuilder &builder = converter.getFirOpBuilder(); | 
|  |  | 
|  | mlir::Value funcAddr = fir::getBase(procExv); | 
|  | if (!mlir::isa<fir::BoxProcType>(funcAddr.getType())) { | 
|  | mlir::Type boxTy = | 
|  | Fortran::lower::getUntypedBoxProcType(&converter.getMLIRContext()); | 
|  | if (auto host = Fortran::lower::argumentHostAssocs(converter, funcAddr)) | 
|  | funcAddr = builder.create<fir::EmboxProcOp>( | 
|  | loc, boxTy, llvm::ArrayRef<mlir::Value>{funcAddr, host}); | 
|  | else | 
|  | funcAddr = builder.create<fir::EmboxProcOp>(loc, boxTy, funcAddr); | 
|  | } | 
|  |  | 
|  | mlir::Value res = procExv.match( | 
|  | [&](const fir::CharBoxValue &box) -> mlir::Value { | 
|  | mlir::Type tupleTy = | 
|  | fir::factory::getCharacterProcedureTupleType(funcAddr.getType()); | 
|  | return fir::factory::createCharacterProcedureTuple( | 
|  | builder, loc, tupleTy, funcAddr, box.getLen()); | 
|  | }, | 
|  | [funcAddr](const auto &) { return funcAddr; }); | 
|  | return hlfir::EntityWithAttributes{res}; | 
|  | } | 
|  |  | 
|  | mlir::Value Fortran::lower::convertProcedureDesignatorInitialTarget( | 
|  | Fortran::lower::AbstractConverter &converter, mlir::Location loc, | 
|  | const Fortran::semantics::Symbol &sym) { | 
|  | Fortran::lower::SymMap globalOpSymMap; | 
|  | Fortran::lower::StatementContext stmtCtx; | 
|  | Fortran::evaluate::ProcedureDesignator proc(sym); | 
|  | auto procVal{Fortran::lower::convertProcedureDesignatorToHLFIR( | 
|  | loc, converter, proc, globalOpSymMap, stmtCtx)}; | 
|  | return fir::getBase(Fortran::lower::convertToAddress( | 
|  | loc, converter, procVal, stmtCtx, procVal.getType())); | 
|  | } | 
|  |  | 
|  | mlir::Value Fortran::lower::derefPassProcPointerComponent( | 
|  | mlir::Location loc, Fortran::lower::AbstractConverter &converter, | 
|  | const Fortran::evaluate::ProcedureDesignator &proc, mlir::Value passedArg, | 
|  | Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { | 
|  | const Fortran::semantics::Symbol *procComponentSym = proc.GetSymbol(); | 
|  | assert(procComponentSym && | 
|  | "failed to retrieve pointer procedure component symbol"); | 
|  | hlfir::EntityWithAttributes pointerComp = designateProcedurePointerComponent( | 
|  | loc, converter, *procComponentSym, passedArg, symMap, stmtCtx); | 
|  | return converter.getFirOpBuilder().create<fir::LoadOp>(loc, pointerComp); | 
|  | } |