1cfc48600SJean Perier //===- ConvertProcedureDesignator.cpp -- Procedure Designator ---*- C++ -*-===//
2cfc48600SJean Perier //
3cfc48600SJean Perier // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4cfc48600SJean Perier // See https://llvm.org/LICENSE.txt for license information.
5cfc48600SJean Perier // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6cfc48600SJean Perier //
7cfc48600SJean Perier //===----------------------------------------------------------------------===//
8cfc48600SJean Perier
9cfc48600SJean Perier #include "flang/Lower/ConvertProcedureDesignator.h"
10cfc48600SJean Perier #include "flang/Evaluate/intrinsics.h"
11cfc48600SJean Perier #include "flang/Lower/AbstractConverter.h"
12cfc48600SJean Perier #include "flang/Lower/CallInterface.h"
13cfc48600SJean Perier #include "flang/Lower/ConvertCall.h"
14af09219eSDaniel Chen #include "flang/Lower/ConvertExprToHLFIR.h"
15cfc48600SJean Perier #include "flang/Lower/ConvertVariable.h"
16cfc48600SJean Perier #include "flang/Lower/Support/Utils.h"
17cfc48600SJean Perier #include "flang/Lower/SymbolMap.h"
18cfc48600SJean Perier #include "flang/Optimizer/Builder/Character.h"
19cfc48600SJean Perier #include "flang/Optimizer/Builder/IntrinsicCall.h"
20af09219eSDaniel Chen #include "flang/Optimizer/Builder/Todo.h"
21cfc48600SJean Perier #include "flang/Optimizer/Dialect/FIROps.h"
22c373f581SjeanPerier #include "flang/Optimizer/HLFIR/HLFIROps.h"
23cfc48600SJean Perier
areAllSymbolsInExprMapped(const Fortran::evaluate::ExtentExpr & expr,Fortran::lower::SymMap & symMap)24cfc48600SJean Perier static bool areAllSymbolsInExprMapped(const Fortran::evaluate::ExtentExpr &expr,
25cfc48600SJean Perier Fortran::lower::SymMap &symMap) {
26cfc48600SJean Perier for (const auto &sym : Fortran::evaluate::CollectSymbols(expr))
27cfc48600SJean Perier if (!symMap.lookupSymbol(sym))
28cfc48600SJean Perier return false;
29cfc48600SJean Perier return true;
30cfc48600SJean Perier }
31cfc48600SJean Perier
convertProcedureDesignator(mlir::Location loc,Fortran::lower::AbstractConverter & converter,const Fortran::evaluate::ProcedureDesignator & proc,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)32cfc48600SJean Perier fir::ExtendedValue Fortran::lower::convertProcedureDesignator(
33cfc48600SJean Perier mlir::Location loc, Fortran::lower::AbstractConverter &converter,
34cfc48600SJean Perier const Fortran::evaluate::ProcedureDesignator &proc,
35cfc48600SJean Perier Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
36cfc48600SJean Perier fir::FirOpBuilder &builder = converter.getFirOpBuilder();
37cfc48600SJean Perier
38cfc48600SJean Perier if (const Fortran::evaluate::SpecificIntrinsic *intrinsic =
39cfc48600SJean Perier proc.GetSpecificIntrinsic()) {
40cfc48600SJean Perier mlir::FunctionType signature =
41cfc48600SJean Perier Fortran::lower::translateSignature(proc, converter);
42cfc48600SJean Perier // Intrinsic lowering is based on the generic name, so retrieve it here in
43cfc48600SJean Perier // case it is different from the specific name. The type of the specific
44cfc48600SJean Perier // intrinsic is retained in the signature.
45cfc48600SJean Perier std::string genericName =
46cfc48600SJean Perier converter.getFoldingContext().intrinsics().GetGenericIntrinsicName(
47cfc48600SJean Perier intrinsic->name);
48cfc48600SJean Perier mlir::SymbolRefAttr symbolRefAttr =
49cfc48600SJean Perier fir::getUnrestrictedIntrinsicSymbolRefAttr(builder, loc, genericName,
50cfc48600SJean Perier signature);
51cfc48600SJean Perier mlir::Value funcPtr =
52cfc48600SJean Perier builder.create<fir::AddrOfOp>(loc, signature, symbolRefAttr);
53cfc48600SJean Perier return funcPtr;
54cfc48600SJean Perier }
55cfc48600SJean Perier const Fortran::semantics::Symbol *symbol = proc.GetSymbol();
56cfc48600SJean Perier assert(symbol && "expected symbol in ProcedureDesignator");
57cfc48600SJean Perier mlir::Value funcPtr;
58cfc48600SJean Perier mlir::Value funcPtrResultLength;
59cfc48600SJean Perier if (Fortran::semantics::IsDummy(*symbol)) {
60cfc48600SJean Perier Fortran::lower::SymbolBox val = symMap.lookupSymbol(*symbol);
61cfc48600SJean Perier assert(val && "Dummy procedure not in symbol map");
62cfc48600SJean Perier funcPtr = val.getAddr();
63cfc48600SJean Perier if (fir::isCharacterProcedureTuple(funcPtr.getType(),
64cfc48600SJean Perier /*acceptRawFunc=*/false))
65cfc48600SJean Perier std::tie(funcPtr, funcPtrResultLength) =
66cfc48600SJean Perier fir::factory::extractCharacterProcedureTuple(builder, loc, funcPtr);
67cfc48600SJean Perier } else {
68cfc48600SJean Perier mlir::func::FuncOp func =
6988684317SjeanPerier Fortran::lower::getOrDeclareFunction(proc, converter);
7088684317SjeanPerier mlir::SymbolRefAttr nameAttr = builder.getSymbolRefAttr(func.getSymName());
7188684317SjeanPerier funcPtr =
7288684317SjeanPerier builder.create<fir::AddrOfOp>(loc, func.getFunctionType(), nameAttr);
73cfc48600SJean Perier }
74cfc48600SJean Perier if (Fortran::lower::mustPassLengthWithDummyProcedure(proc, converter)) {
75cfc48600SJean Perier // The result length, if available here, must be propagated along the
76cfc48600SJean Perier // procedure address so that call sites where the result length is assumed
77cfc48600SJean Perier // can retrieve the length.
78cfc48600SJean Perier Fortran::evaluate::DynamicType resultType = proc.GetType().value();
79cfc48600SJean Perier if (const auto &lengthExpr = resultType.GetCharLength()) {
80cfc48600SJean Perier // The length expression may refer to dummy argument symbols that are
81cfc48600SJean Perier // meaningless without any actual arguments. Leave the length as
82cfc48600SJean Perier // unknown in that case, it be resolved on the call site
83cfc48600SJean Perier // with the actual arguments.
84cfc48600SJean Perier if (areAllSymbolsInExprMapped(*lengthExpr, symMap)) {
85cfc48600SJean Perier mlir::Value rawLen = fir::getBase(
86cfc48600SJean Perier converter.genExprValue(toEvExpr(*lengthExpr), stmtCtx));
87cfc48600SJean Perier // F2018 7.4.4.2 point 5.
88cfc48600SJean Perier funcPtrResultLength =
89cfc48600SJean Perier fir::factory::genMaxWithZero(builder, loc, rawLen);
90cfc48600SJean Perier }
91cfc48600SJean Perier }
92*17bd3120SSlava Zakharin // The caller of the function pointer will have to allocate
93*17bd3120SSlava Zakharin // the function result with the character length specified
94*17bd3120SSlava Zakharin // by the boxed value. If the result length cannot be
95*17bd3120SSlava Zakharin // computed statically, set it to zero (we used to use -1,
96*17bd3120SSlava Zakharin // but this could cause assertions in LLVM after inlining
97*17bd3120SSlava Zakharin // exposed alloca of size -1).
98cfc48600SJean Perier if (!funcPtrResultLength)
99cfc48600SJean Perier funcPtrResultLength = builder.createIntegerConstant(
100*17bd3120SSlava Zakharin loc, builder.getCharacterLengthType(), 0);
101cfc48600SJean Perier return fir::CharBoxValue{funcPtr, funcPtrResultLength};
102cfc48600SJean Perier }
103cfc48600SJean Perier return funcPtr;
104cfc48600SJean Perier }
105cedfd272SJean Perier
designateProcedurePointerComponent(mlir::Location loc,Fortran::lower::AbstractConverter & converter,const Fortran::evaluate::Symbol & procComponentSym,mlir::Value base,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)106c373f581SjeanPerier static hlfir::EntityWithAttributes designateProcedurePointerComponent(
107c373f581SjeanPerier mlir::Location loc, Fortran::lower::AbstractConverter &converter,
108c373f581SjeanPerier const Fortran::evaluate::Symbol &procComponentSym, mlir::Value base,
109c373f581SjeanPerier Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
110c373f581SjeanPerier fir::FirOpBuilder &builder = converter.getFirOpBuilder();
111c373f581SjeanPerier fir::FortranVariableFlagsAttr attributes =
112c373f581SjeanPerier Fortran::lower::translateSymbolAttributes(builder.getContext(),
113c373f581SjeanPerier procComponentSym);
114c373f581SjeanPerier /// Passed argument may be a descriptor. This is a scalar reference, so the
115c373f581SjeanPerier /// base address can be directly addressed.
116fac349a1SChristian Sigg if (mlir::isa<fir::BaseBoxType>(base.getType()))
117c373f581SjeanPerier base = builder.create<fir::BoxAddrOp>(loc, base);
118c373f581SjeanPerier std::string fieldName = converter.getRecordTypeFieldName(procComponentSym);
119c373f581SjeanPerier auto recordType =
120fac349a1SChristian Sigg mlir::cast<fir::RecordType>(hlfir::getFortranElementType(base.getType()));
121c373f581SjeanPerier mlir::Type fieldType = recordType.getType(fieldName);
122eaa8def9SjeanPerier // Note: semantics turns x%p() into x%t%p() when the procedure pointer
123eaa8def9SjeanPerier // component is part of parent component t.
124c373f581SjeanPerier if (!fieldType)
125eaa8def9SjeanPerier TODO(loc, "passing type bound procedure (extension)");
126c373f581SjeanPerier mlir::Type designatorType = fir::ReferenceType::get(fieldType);
127c373f581SjeanPerier mlir::Value compRef = builder.create<hlfir::DesignateOp>(
128c373f581SjeanPerier loc, designatorType, base, fieldName,
129c373f581SjeanPerier /*compShape=*/mlir::Value{}, hlfir::DesignateOp::Subscripts{},
130c373f581SjeanPerier /*substring=*/mlir::ValueRange{},
131c373f581SjeanPerier /*complexPart=*/std::nullopt,
132c373f581SjeanPerier /*shape=*/mlir::Value{}, /*typeParams=*/mlir::ValueRange{}, attributes);
133c373f581SjeanPerier return hlfir::EntityWithAttributes{compRef};
134c373f581SjeanPerier }
135c373f581SjeanPerier
convertProcedurePointerComponent(mlir::Location loc,Fortran::lower::AbstractConverter & converter,const Fortran::evaluate::Component & procComponent,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)136c373f581SjeanPerier static hlfir::EntityWithAttributes convertProcedurePointerComponent(
137c373f581SjeanPerier mlir::Location loc, Fortran::lower::AbstractConverter &converter,
138c373f581SjeanPerier const Fortran::evaluate::Component &procComponent,
139c373f581SjeanPerier Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
140c373f581SjeanPerier fir::ExtendedValue baseExv = Fortran::lower::convertDataRefToValue(
141c373f581SjeanPerier loc, converter, procComponent.base(), symMap, stmtCtx);
142c373f581SjeanPerier mlir::Value base = fir::getBase(baseExv);
143c373f581SjeanPerier const Fortran::semantics::Symbol &procComponentSym =
144c373f581SjeanPerier procComponent.GetLastSymbol();
145c373f581SjeanPerier return designateProcedurePointerComponent(loc, converter, procComponentSym,
146c373f581SjeanPerier base, symMap, stmtCtx);
147c373f581SjeanPerier }
148c373f581SjeanPerier
convertProcedureDesignatorToHLFIR(mlir::Location loc,Fortran::lower::AbstractConverter & converter,const Fortran::evaluate::ProcedureDesignator & proc,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)149cedfd272SJean Perier hlfir::EntityWithAttributes Fortran::lower::convertProcedureDesignatorToHLFIR(
150cedfd272SJean Perier mlir::Location loc, Fortran::lower::AbstractConverter &converter,
151cedfd272SJean Perier const Fortran::evaluate::ProcedureDesignator &proc,
152cedfd272SJean Perier Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
153af09219eSDaniel Chen const auto *sym = proc.GetSymbol();
154af09219eSDaniel Chen if (sym) {
155af09219eSDaniel Chen if (sym->GetUltimate().attrs().test(Fortran::semantics::Attr::INTRINSIC))
156af09219eSDaniel Chen TODO(loc, "Procedure pointer with intrinsic target.");
157af09219eSDaniel Chen if (std::optional<fir::FortranVariableOpInterface> varDef =
158af09219eSDaniel Chen symMap.lookupVariableDefinition(*sym))
159af09219eSDaniel Chen return *varDef;
160af09219eSDaniel Chen }
161af09219eSDaniel Chen
162c373f581SjeanPerier if (const Fortran::evaluate::Component *procComponent = proc.GetComponent())
163c373f581SjeanPerier return convertProcedurePointerComponent(loc, converter, *procComponent,
164c373f581SjeanPerier symMap, stmtCtx);
165c373f581SjeanPerier
166cedfd272SJean Perier fir::ExtendedValue procExv =
167cedfd272SJean Perier convertProcedureDesignator(loc, converter, proc, symMap, stmtCtx);
168cedfd272SJean Perier // Directly package the procedure address as a fir.boxproc or
169cedfd272SJean Perier // tuple<fir.boxbroc, len> so that it can be returned as a single mlir::Value.
170cedfd272SJean Perier fir::FirOpBuilder &builder = converter.getFirOpBuilder();
171cedfd272SJean Perier
172cedfd272SJean Perier mlir::Value funcAddr = fir::getBase(procExv);
173fac349a1SChristian Sigg if (!mlir::isa<fir::BoxProcType>(funcAddr.getType())) {
174cedfd272SJean Perier mlir::Type boxTy =
175cedfd272SJean Perier Fortran::lower::getUntypedBoxProcType(&converter.getMLIRContext());
176cedfd272SJean Perier if (auto host = Fortran::lower::argumentHostAssocs(converter, funcAddr))
177cedfd272SJean Perier funcAddr = builder.create<fir::EmboxProcOp>(
178cedfd272SJean Perier loc, boxTy, llvm::ArrayRef<mlir::Value>{funcAddr, host});
179cedfd272SJean Perier else
180cedfd272SJean Perier funcAddr = builder.create<fir::EmboxProcOp>(loc, boxTy, funcAddr);
181cedfd272SJean Perier }
182cedfd272SJean Perier
183cedfd272SJean Perier mlir::Value res = procExv.match(
184cedfd272SJean Perier [&](const fir::CharBoxValue &box) -> mlir::Value {
185cedfd272SJean Perier mlir::Type tupleTy =
186cedfd272SJean Perier fir::factory::getCharacterProcedureTupleType(funcAddr.getType());
187cedfd272SJean Perier return fir::factory::createCharacterProcedureTuple(
188cedfd272SJean Perier builder, loc, tupleTy, funcAddr, box.getLen());
189cedfd272SJean Perier },
190cedfd272SJean Perier [funcAddr](const auto &) { return funcAddr; });
191cedfd272SJean Perier return hlfir::EntityWithAttributes{res};
192cedfd272SJean Perier }
193af09219eSDaniel Chen
convertProcedureDesignatorInitialTarget(Fortran::lower::AbstractConverter & converter,mlir::Location loc,const Fortran::semantics::Symbol & sym)194af09219eSDaniel Chen mlir::Value Fortran::lower::convertProcedureDesignatorInitialTarget(
195af09219eSDaniel Chen Fortran::lower::AbstractConverter &converter, mlir::Location loc,
196af09219eSDaniel Chen const Fortran::semantics::Symbol &sym) {
197af09219eSDaniel Chen Fortran::lower::SymMap globalOpSymMap;
198af09219eSDaniel Chen Fortran::lower::StatementContext stmtCtx;
199af09219eSDaniel Chen Fortran::evaluate::ProcedureDesignator proc(sym);
200af09219eSDaniel Chen auto procVal{Fortran::lower::convertProcedureDesignatorToHLFIR(
201af09219eSDaniel Chen loc, converter, proc, globalOpSymMap, stmtCtx)};
202af09219eSDaniel Chen return fir::getBase(Fortran::lower::convertToAddress(
203af09219eSDaniel Chen loc, converter, procVal, stmtCtx, procVal.getType()));
204af09219eSDaniel Chen }
205c373f581SjeanPerier
derefPassProcPointerComponent(mlir::Location loc,Fortran::lower::AbstractConverter & converter,const Fortran::evaluate::ProcedureDesignator & proc,mlir::Value passedArg,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)206c373f581SjeanPerier mlir::Value Fortran::lower::derefPassProcPointerComponent(
207c373f581SjeanPerier mlir::Location loc, Fortran::lower::AbstractConverter &converter,
208c373f581SjeanPerier const Fortran::evaluate::ProcedureDesignator &proc, mlir::Value passedArg,
209c373f581SjeanPerier Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
210c373f581SjeanPerier const Fortran::semantics::Symbol *procComponentSym = proc.GetSymbol();
211c373f581SjeanPerier assert(procComponentSym &&
212c373f581SjeanPerier "failed to retrieve pointer procedure component symbol");
213c373f581SjeanPerier hlfir::EntityWithAttributes pointerComp = designateProcedurePointerComponent(
214c373f581SjeanPerier loc, converter, *procComponentSym, passedArg, symMap, stmtCtx);
215c373f581SjeanPerier return converter.getFirOpBuilder().create<fir::LoadOp>(loc, pointerComp);
216c373f581SjeanPerier }
217