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