xref: /llvm-project/flang/lib/Lower/OpenMP/Utils.cpp (revision 662133a278f4f3553f061f7999759bae4e842820)
14d4af15cSKareem Ergawy //===-- Utils..cpp ----------------------------------------------*- C++ -*-===//
24d4af15cSKareem Ergawy //
34d4af15cSKareem Ergawy // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44d4af15cSKareem Ergawy // See https://llvm.org/LICENSE.txt for license information.
54d4af15cSKareem Ergawy // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64d4af15cSKareem Ergawy //
74d4af15cSKareem Ergawy //===----------------------------------------------------------------------===//
84d4af15cSKareem Ergawy //
94d4af15cSKareem Ergawy // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
104d4af15cSKareem Ergawy //
114d4af15cSKareem Ergawy //===----------------------------------------------------------------------===//
124d4af15cSKareem Ergawy 
134d4af15cSKareem Ergawy #include "Utils.h"
144d4af15cSKareem Ergawy 
15554be97dSKrzysztof Parzyszek #include "Clauses.h"
16e508baccSagozillon 
174d4af15cSKareem Ergawy #include <flang/Lower/AbstractConverter.h>
184d4af15cSKareem Ergawy #include <flang/Lower/ConvertType.h>
19e532241bSKareem Ergawy #include <flang/Lower/DirectivesCommon.h>
20554be97dSKrzysztof Parzyszek #include <flang/Lower/PFTBuilder.h>
2173402634SSergio Afonso #include <flang/Optimizer/Builder/FIRBuilder.h>
22f9824439SKrzysztof Parzyszek #include <flang/Optimizer/Builder/Todo.h>
234d4af15cSKareem Ergawy #include <flang/Parser/parse-tree.h>
244d4af15cSKareem Ergawy #include <flang/Parser/tools.h>
254d4af15cSKareem Ergawy #include <flang/Semantics/tools.h>
264d4af15cSKareem Ergawy #include <llvm/Support/CommandLine.h>
274d4af15cSKareem Ergawy 
28e508baccSagozillon #include <iterator>
29435e850bSAndrew Gozillon 
304d4af15cSKareem Ergawy llvm::cl::opt<bool> treatIndexAsSection(
314d4af15cSKareem Ergawy     "openmp-treat-index-as-section",
324d4af15cSKareem Ergawy     llvm::cl::desc("In the OpenMP data clauses treat `a(N)` as `a(N:N)`."),
334d4af15cSKareem Ergawy     llvm::cl::init(true));
344d4af15cSKareem Ergawy 
3526b8be20SKareem Ergawy llvm::cl::opt<bool> enableDelayedPrivatization(
3626b8be20SKareem Ergawy     "openmp-enable-delayed-privatization",
3726b8be20SKareem Ergawy     llvm::cl::desc(
3826b8be20SKareem Ergawy         "Emit `[first]private` variables as clauses on the MLIR ops."),
3910df3207SKareem Ergawy     llvm::cl::init(true));
4026b8be20SKareem Ergawy 
411539da46SKareem Ergawy llvm::cl::opt<bool> enableDelayedPrivatizationStaging(
421539da46SKareem Ergawy     "openmp-enable-delayed-privatization-staging",
431539da46SKareem Ergawy     llvm::cl::desc("For partially supported constructs, emit `[first]private` "
441539da46SKareem Ergawy                    "variables as clauses on the MLIR ops."),
451539da46SKareem Ergawy     llvm::cl::init(false));
461539da46SKareem Ergawy 
474d4af15cSKareem Ergawy namespace Fortran {
484d4af15cSKareem Ergawy namespace lower {
494d4af15cSKareem Ergawy namespace omp {
504d4af15cSKareem Ergawy 
51992413deSKrzysztof Parzyszek int64_t getCollapseValue(const List<Clause> &clauses) {
52992413deSKrzysztof Parzyszek   auto iter = llvm::find_if(clauses, [](const Clause &clause) {
53992413deSKrzysztof Parzyszek     return clause.id == llvm::omp::Clause::OMPC_collapse;
54992413deSKrzysztof Parzyszek   });
55992413deSKrzysztof Parzyszek   if (iter != clauses.end()) {
56992413deSKrzysztof Parzyszek     const auto &collapse = std::get<clause::Collapse>(iter->u);
57992413deSKrzysztof Parzyszek     return evaluate::ToInt64(collapse.v).value();
58992413deSKrzysztof Parzyszek   }
59992413deSKrzysztof Parzyszek   return 1;
60992413deSKrzysztof Parzyszek }
61992413deSKrzysztof Parzyszek 
6263e70c05SKrzysztof Parzyszek void genObjectList(const ObjectList &objects,
637a66e420SKrzysztof Parzyszek                    lower::AbstractConverter &converter,
6463e70c05SKrzysztof Parzyszek                    llvm::SmallVectorImpl<mlir::Value> &operands) {
6563e70c05SKrzysztof Parzyszek   for (const Object &object : objects) {
668b18f2feSKrzysztof Parzyszek     const semantics::Symbol *sym = object.sym();
6763e70c05SKrzysztof Parzyszek     assert(sym && "Expected Symbol");
6863e70c05SKrzysztof Parzyszek     if (mlir::Value variable = converter.getSymbolAddress(*sym)) {
6963e70c05SKrzysztof Parzyszek       operands.push_back(variable);
7063e70c05SKrzysztof Parzyszek     } else if (const auto *details =
717a66e420SKrzysztof Parzyszek                    sym->detailsIf<semantics::HostAssocDetails>()) {
7263e70c05SKrzysztof Parzyszek       operands.push_back(converter.getSymbolAddress(details->symbol()));
7363e70c05SKrzysztof Parzyszek       converter.copySymbolBinding(details->symbol(), *sym);
7463e70c05SKrzysztof Parzyszek     }
7563e70c05SKrzysztof Parzyszek   }
7663e70c05SKrzysztof Parzyszek }
7763e70c05SKrzysztof Parzyszek 
787a66e420SKrzysztof Parzyszek mlir::Type getLoopVarType(lower::AbstractConverter &converter,
7973402634SSergio Afonso                           std::size_t loopVarTypeSize) {
8073402634SSergio Afonso   // OpenMP runtime requires 32-bit or 64-bit loop variables.
8173402634SSergio Afonso   loopVarTypeSize = loopVarTypeSize * 8;
8273402634SSergio Afonso   if (loopVarTypeSize < 32) {
8373402634SSergio Afonso     loopVarTypeSize = 32;
8473402634SSergio Afonso   } else if (loopVarTypeSize > 64) {
8573402634SSergio Afonso     loopVarTypeSize = 64;
8673402634SSergio Afonso     mlir::emitWarning(converter.getCurrentLocation(),
8773402634SSergio Afonso                       "OpenMP loop iteration variable cannot have more than 64 "
8873402634SSergio Afonso                       "bits size and will be narrowed into 64 bits.");
8973402634SSergio Afonso   }
9073402634SSergio Afonso   assert((loopVarTypeSize == 32 || loopVarTypeSize == 64) &&
9173402634SSergio Afonso          "OpenMP loop iteration variable size must be transformed into 32-bit "
9273402634SSergio Afonso          "or 64-bit");
9373402634SSergio Afonso   return converter.getFirOpBuilder().getIntegerType(loopVarTypeSize);
9473402634SSergio Afonso }
9573402634SSergio Afonso 
967a66e420SKrzysztof Parzyszek semantics::Symbol *
977a66e420SKrzysztof Parzyszek getIterationVariableSymbol(const lower::pft::Evaluation &eval) {
987a66e420SKrzysztof Parzyszek   return eval.visit(common::visitors{
997a66e420SKrzysztof Parzyszek       [&](const parser::DoConstruct &doLoop) {
100554be97dSKrzysztof Parzyszek         if (const auto &maybeCtrl = doLoop.GetLoopControl()) {
1017a66e420SKrzysztof Parzyszek           using LoopControl = parser::LoopControl;
102554be97dSKrzysztof Parzyszek           if (auto *bounds = std::get_if<LoopControl::Bounds>(&maybeCtrl->u)) {
1037a66e420SKrzysztof Parzyszek             static_assert(std::is_same_v<decltype(bounds->name),
1047a66e420SKrzysztof Parzyszek                                          parser::Scalar<parser::Name>>);
105554be97dSKrzysztof Parzyszek             return bounds->name.thing.symbol;
106554be97dSKrzysztof Parzyszek           }
107554be97dSKrzysztof Parzyszek         }
1087a66e420SKrzysztof Parzyszek         return static_cast<semantics::Symbol *>(nullptr);
109554be97dSKrzysztof Parzyszek       },
1107a66e420SKrzysztof Parzyszek       [](auto &&) { return static_cast<semantics::Symbol *>(nullptr); },
111554be97dSKrzysztof Parzyszek   });
112554be97dSKrzysztof Parzyszek }
113554be97dSKrzysztof Parzyszek 
1144d4af15cSKareem Ergawy void gatherFuncAndVarSyms(
11563e70c05SKrzysztof Parzyszek     const ObjectList &objects, mlir::omp::DeclareTargetCaptureClause clause,
1164d4af15cSKareem Ergawy     llvm::SmallVectorImpl<DeclareTargetCapturePair> &symbolAndClause) {
11763e70c05SKrzysztof Parzyszek   for (const Object &object : objects)
1188b18f2feSKrzysztof Parzyszek     symbolAndClause.emplace_back(clause, *object.sym());
1194d4af15cSKareem Ergawy }
1204d4af15cSKareem Ergawy 
121435e850bSAndrew Gozillon mlir::omp::MapInfoOp
122435e850bSAndrew Gozillon createMapInfoOp(fir::FirOpBuilder &builder, mlir::Location loc,
123e508baccSagozillon                 mlir::Value baseAddr, mlir::Value varPtrPtr,
124e508baccSagozillon                 llvm::StringRef name, llvm::ArrayRef<mlir::Value> bounds,
125435e850bSAndrew Gozillon                 llvm::ArrayRef<mlir::Value> members,
126e508baccSagozillon                 mlir::ArrayAttr membersIndex, uint64_t mapType,
127435e850bSAndrew Gozillon                 mlir::omp::VariableCaptureKind mapCaptureType, mlir::Type retTy,
128435e850bSAndrew Gozillon                 bool partialMap) {
129e3ca558fSagozillon   if (auto boxTy = llvm::dyn_cast<fir::BaseBoxType>(baseAddr.getType())) {
130435e850bSAndrew Gozillon     baseAddr = builder.create<fir::BoxAddrOp>(loc, baseAddr);
131435e850bSAndrew Gozillon     retTy = baseAddr.getType();
132435e850bSAndrew Gozillon   }
133435e850bSAndrew Gozillon 
134435e850bSAndrew Gozillon   mlir::TypeAttr varType = mlir::TypeAttr::get(
135435e850bSAndrew Gozillon       llvm::cast<mlir::omp::PointerLikeType>(retTy).getElementType());
136435e850bSAndrew Gozillon 
13714243368SAkash Banerjee   // For types with unknown extents such as <2x?xi32> we discard the incomplete
13814243368SAkash Banerjee   // type info and only retain the base type. The correct dimensions are later
13914243368SAkash Banerjee   // recovered through the bounds info.
14014243368SAkash Banerjee   if (auto seqType = llvm::dyn_cast<fir::SequenceType>(varType.getValue()))
14114243368SAkash Banerjee     if (seqType.hasDynamicExtents())
14214243368SAkash Banerjee       varType = mlir::TypeAttr::get(seqType.getEleTy());
14314243368SAkash Banerjee 
144435e850bSAndrew Gozillon   mlir::omp::MapInfoOp op = builder.create<mlir::omp::MapInfoOp>(
145435e850bSAndrew Gozillon       loc, retTy, baseAddr, varType, varPtrPtr, members, membersIndex, bounds,
146435e850bSAndrew Gozillon       builder.getIntegerAttr(builder.getIntegerType(64, false), mapType),
147435e850bSAndrew Gozillon       builder.getAttr<mlir::omp::VariableCaptureKindAttr>(mapCaptureType),
148435e850bSAndrew Gozillon       builder.getStringAttr(name), builder.getBoolAttr(partialMap));
149435e850bSAndrew Gozillon   return op;
150435e850bSAndrew Gozillon }
151435e850bSAndrew Gozillon 
152e508baccSagozillon // This function gathers the individual omp::Object's that make up a
153e508baccSagozillon // larger omp::Object symbol.
154e508baccSagozillon //
155e508baccSagozillon // For example, provided the larger symbol: "parent%child%member", this
156e508baccSagozillon // function breaks it up into its constituent components ("parent",
157e508baccSagozillon // "child", "member"), so we can access each individual component and
158e508baccSagozillon // introspect details. Important to note is this function breaks it up from
159e508baccSagozillon // RHS to LHS ("member" to "parent") and then we reverse it so that the
160e508baccSagozillon // returned omp::ObjectList is LHS to RHS, with the "parent" at the
161e508baccSagozillon // beginning.
162e508baccSagozillon omp::ObjectList gatherObjectsOf(omp::Object derivedTypeMember,
163e508baccSagozillon                                 semantics::SemanticsContext &semaCtx) {
164e508baccSagozillon   omp::ObjectList objList;
165e508baccSagozillon   std::optional<omp::Object> baseObj = derivedTypeMember;
166e508baccSagozillon   while (baseObj.has_value()) {
167e508baccSagozillon     objList.push_back(baseObj.value());
168e508baccSagozillon     baseObj = getBaseObject(baseObj.value(), semaCtx);
169e508baccSagozillon   }
170e508baccSagozillon   return omp::ObjectList{llvm::reverse(objList)};
171e508baccSagozillon }
172e508baccSagozillon 
173e508baccSagozillon // This function generates a series of indices from a provided omp::Object,
174e508baccSagozillon // that devolves to an ArrayRef symbol, e.g. "array(2,3,4)", this function
175e508baccSagozillon // would generate a series of indices of "[1][2][3]" for the above example,
176e508baccSagozillon // offsetting by -1 to account for the non-zero fortran indexes.
177e508baccSagozillon //
178e508baccSagozillon // These indices can then be provided to a coordinate operation or other
179e508baccSagozillon // GEP-like operation to access the relevant positional member of the
180e508baccSagozillon // array.
181e508baccSagozillon //
182e508baccSagozillon // It is of note that the function only supports subscript integers currently
183e508baccSagozillon // and not Triplets i.e. Array(1:2:3).
184e508baccSagozillon static void generateArrayIndices(lower::AbstractConverter &converter,
185e508baccSagozillon                                  fir::FirOpBuilder &firOpBuilder,
186e508baccSagozillon                                  lower::StatementContext &stmtCtx,
187e508baccSagozillon                                  mlir::Location clauseLocation,
188e508baccSagozillon                                  llvm::SmallVectorImpl<mlir::Value> &indices,
189e508baccSagozillon                                  omp::Object object) {
190e508baccSagozillon   auto maybeRef = evaluate::ExtractDataRef(*object.ref());
191e508baccSagozillon   if (!maybeRef)
192e508baccSagozillon     return;
193e508baccSagozillon 
194e508baccSagozillon   auto *arr = std::get_if<evaluate::ArrayRef>(&maybeRef->u);
195e508baccSagozillon   if (!arr)
196e508baccSagozillon     return;
197e508baccSagozillon 
198e508baccSagozillon   for (auto v : arr->subscript()) {
199e508baccSagozillon     if (std::holds_alternative<Triplet>(v.u))
200e508baccSagozillon       TODO(clauseLocation, "Triplet indexing in map clause is unsupported");
201e508baccSagozillon 
202e508baccSagozillon     auto expr = std::get<Fortran::evaluate::IndirectSubscriptIntegerExpr>(v.u);
203e508baccSagozillon     mlir::Value subscript =
204e508baccSagozillon         fir::getBase(converter.genExprValue(toEvExpr(expr.value()), stmtCtx));
205e508baccSagozillon     mlir::Value one = firOpBuilder.createIntegerConstant(
206e508baccSagozillon         clauseLocation, firOpBuilder.getIndexType(), 1);
207e508baccSagozillon     subscript = firOpBuilder.createConvert(
208e508baccSagozillon         clauseLocation, firOpBuilder.getIndexType(), subscript);
209e508baccSagozillon     indices.push_back(firOpBuilder.create<mlir::arith::SubIOp>(clauseLocation,
210e508baccSagozillon                                                                subscript, one));
211e508baccSagozillon   }
212e508baccSagozillon }
213e508baccSagozillon 
214e508baccSagozillon /// When mapping members of derived types, there is a chance that one of the
215e508baccSagozillon /// members along the way to a mapped member is an descriptor. In which case
216e508baccSagozillon /// we have to make sure we generate a map for those along the way otherwise
217e508baccSagozillon /// we will be missing a chunk of data required to actually map the member
218e508baccSagozillon /// type to device. This function effectively generates these maps and the
219e508baccSagozillon /// appropriate data accesses required to generate these maps. It will avoid
220e508baccSagozillon /// creating duplicate maps, as duplicates are just as bad as unmapped
221e508baccSagozillon /// descriptor data in a lot of cases for the runtime (and unnecessary
222e508baccSagozillon /// data movement should be avoided where possible).
223e508baccSagozillon ///
224e508baccSagozillon /// As an example for the following mapping:
225e508baccSagozillon ///
226e508baccSagozillon /// type :: vertexes
227e508baccSagozillon ///     integer(4), allocatable :: vertexx(:)
228e508baccSagozillon ///     integer(4), allocatable :: vertexy(:)
229e508baccSagozillon /// end type vertexes
230e508baccSagozillon ///
231e508baccSagozillon /// type :: dtype
232e508baccSagozillon ///     real(4) :: i
233e508baccSagozillon ///     type(vertexes), allocatable :: vertexes(:)
234e508baccSagozillon /// end type dtype
235e508baccSagozillon ///
236e508baccSagozillon /// type(dtype), allocatable :: alloca_dtype
237e508baccSagozillon ///
238e508baccSagozillon /// !$omp target map(tofrom: alloca_dtype%vertexes(N1)%vertexx)
239e508baccSagozillon ///
240e508baccSagozillon /// The below HLFIR/FIR is generated (trimmed for conciseness):
241e508baccSagozillon ///
242e508baccSagozillon /// On the first iteration we index into the record type alloca_dtype
243e508baccSagozillon /// to access "vertexes", we then generate a map for this descriptor
244e508baccSagozillon /// alongside bounds to indicate we only need the 1 member, rather than
245e508baccSagozillon /// the whole array block in this case (In theory we could map its
246e508baccSagozillon /// entirety at the cost of data transfer bandwidth).
247e508baccSagozillon ///
248e508baccSagozillon /// %13:2 = hlfir.declare ... "alloca_dtype" ...
249e508baccSagozillon /// %39 = fir.load %13#0 : ...
250e508baccSagozillon /// %40 = fir.coordinate_of %39, %c1 : ...
251e508baccSagozillon /// %51 = omp.map.info var_ptr(%40 : ...) map_clauses(to) capture(ByRef) ...
252e508baccSagozillon /// %52 = fir.load %40 : ...
253e508baccSagozillon ///
254e508baccSagozillon /// Second iteration generating access to "vertexes(N1) utilising the N1 index
255e508baccSagozillon /// %53 = load N1 ...
256e508baccSagozillon /// %54 = fir.convert %53 : (i32) -> i64
257e508baccSagozillon /// %55 = fir.convert %54 : (i64) -> index
258e508baccSagozillon /// %56 = arith.subi %55, %c1 : index
259e508baccSagozillon /// %57 = fir.coordinate_of %52, %56 : ...
260e508baccSagozillon ///
261e508baccSagozillon /// Still in the second iteration we access the allocatable member "vertexx",
262e508baccSagozillon /// we return %58 from the function and provide it to the final and "main"
263e508baccSagozillon /// map of processMap (generated by the record type segment of the below
264e508baccSagozillon /// function), if this were not the final symbol in the list, i.e. we accessed
265e508baccSagozillon /// a member below vertexx, we would have generated the map below as we did in
266e508baccSagozillon /// the first iteration and then continue to generate further coordinates to
267e508baccSagozillon /// access further components as required.
268e508baccSagozillon ///
269e508baccSagozillon /// %58 = fir.coordinate_of %57, %c0 : ...
270e508baccSagozillon /// %61 = omp.map.info var_ptr(%58 : ...) map_clauses(to) capture(ByRef) ...
271e508baccSagozillon ///
272e508baccSagozillon /// Parent mapping containing prior generated mapped members, generated at
273e508baccSagozillon /// a later step but here to showcase the "end" result
274e508baccSagozillon ///
275e508baccSagozillon /// omp.map.info var_ptr(%13#1 : ...) map_clauses(to) capture(ByRef)
276e508baccSagozillon ///   members(%50, %61 : [0, 1, 0], [0, 1, 0] : ...
277e508baccSagozillon ///
278e508baccSagozillon /// \param objectList - The list of omp::Object symbol data for each parent
279e508baccSagozillon ///  to the mapped member (also includes the mapped member), generated via
280e508baccSagozillon ///  gatherObjectsOf.
281e508baccSagozillon /// \param indices - List of index data associated with the mapped member
282e508baccSagozillon ///   symbol, which identifies the placement of the member in its parent,
283e508baccSagozillon ///   this helps generate the appropriate member accesses. These indices
284e508baccSagozillon ///   can be generated via generateMemberPlacementIndices.
285e508baccSagozillon /// \param asFortran - A string generated from the mapped variable to be
286e508baccSagozillon ///   associated with the main map, generally (but not restricted to)
287e508baccSagozillon ///   generated via gatherDataOperandAddrAndBounds or other
288e508baccSagozillon ///   DirectiveCommons.hpp utilities.
289e508baccSagozillon /// \param mapTypeBits - The map flags that will be associated with the
290e508baccSagozillon ///   generated maps, minus alterations of the TO and FROM bits for the
291e508baccSagozillon ///   intermediate components to prevent accidental overwriting on device
292e508baccSagozillon ///   write back.
293e508baccSagozillon mlir::Value createParentSymAndGenIntermediateMaps(
294e508baccSagozillon     mlir::Location clauseLocation, lower::AbstractConverter &converter,
295e508baccSagozillon     semantics::SemanticsContext &semaCtx, lower::StatementContext &stmtCtx,
296e508baccSagozillon     omp::ObjectList &objectList, llvm::SmallVectorImpl<int64_t> &indices,
297e508baccSagozillon     OmpMapParentAndMemberData &parentMemberIndices, llvm::StringRef asFortran,
298e508baccSagozillon     llvm::omp::OpenMPOffloadMappingFlags mapTypeBits) {
299e508baccSagozillon   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
300e508baccSagozillon 
301e508baccSagozillon   /// Checks if an omp::Object is an array expression with a subscript, e.g.
302e508baccSagozillon   /// array(1,2).
303e508baccSagozillon   auto isArrayExprWithSubscript = [](omp::Object obj) {
304e508baccSagozillon     if (auto maybeRef = evaluate::ExtractDataRef(*obj.ref())) {
305e508baccSagozillon       evaluate::DataRef ref = *maybeRef;
306e508baccSagozillon       if (auto *arr = std::get_if<evaluate::ArrayRef>(&ref.u))
307e508baccSagozillon         return !arr->subscript().empty();
308e508baccSagozillon     }
309e508baccSagozillon     return false;
310e508baccSagozillon   };
311e508baccSagozillon 
312e508baccSagozillon   // Generate the access to the original parent base address.
313*662133a2SjeanPerier   fir::factory::AddrAndBoundsInfo parentBaseAddr =
314*662133a2SjeanPerier       lower::getDataOperandBaseAddr(converter, firOpBuilder,
315*662133a2SjeanPerier                                     *objectList[0].sym(), clauseLocation);
316e508baccSagozillon   mlir::Value curValue = parentBaseAddr.addr;
317e508baccSagozillon 
318e508baccSagozillon   // Iterate over all objects in the objectList, this should consist of all
319e508baccSagozillon   // record types between the parent and the member being mapped (including
320e508baccSagozillon   // the parent). The object list may also contain array objects as well,
321e508baccSagozillon   // this can occur when specifying bounds or a specific element access
322e508baccSagozillon   // within a member map, we skip these.
323e508baccSagozillon   size_t currentIndicesIdx = 0;
324e508baccSagozillon   for (size_t i = 0; i < objectList.size(); ++i) {
325e508baccSagozillon     // If we encounter a sequence type, i.e. an array, we must generate the
326e508baccSagozillon     // correct coordinate operation to index into the array to proceed further,
327e508baccSagozillon     // this is only relevant in cases where we encounter subscripts currently.
328e508baccSagozillon     //
329e508baccSagozillon     // For example in the following case:
330e508baccSagozillon     //
331e508baccSagozillon     //   map(tofrom: array_dtype(4)%internal_dtypes(3)%float_elements(4))
332e508baccSagozillon     //
333e508baccSagozillon     // We must generate coordinate operation accesses for each subscript
334e508baccSagozillon     // we encounter.
335e508baccSagozillon     if (fir::SequenceType arrType = mlir::dyn_cast<fir::SequenceType>(
336e508baccSagozillon             fir::unwrapPassByRefType(curValue.getType()))) {
337e508baccSagozillon       if (isArrayExprWithSubscript(objectList[i])) {
338e508baccSagozillon         llvm::SmallVector<mlir::Value> subscriptIndices;
339e508baccSagozillon         generateArrayIndices(converter, firOpBuilder, stmtCtx, clauseLocation,
340e508baccSagozillon                              subscriptIndices, objectList[i]);
341e508baccSagozillon         assert(!subscriptIndices.empty() &&
342e508baccSagozillon                "missing expected indices for map clause");
343e508baccSagozillon         curValue = firOpBuilder.create<fir::CoordinateOp>(
344e508baccSagozillon             clauseLocation, firOpBuilder.getRefType(arrType.getEleTy()),
345e508baccSagozillon             curValue, subscriptIndices);
346e508baccSagozillon       }
347e508baccSagozillon     }
348e508baccSagozillon 
349e508baccSagozillon     // If we encounter a record type, we must access the subsequent member
350e508baccSagozillon     // by indexing into it and creating a coordinate operation to do so, we
351e508baccSagozillon     // utilise the index information generated previously and passed in to
352e508baccSagozillon     // work out the correct member to access and the corresponding member
353e508baccSagozillon     // type.
354e508baccSagozillon     if (fir::RecordType recordType = mlir::dyn_cast<fir::RecordType>(
355e508baccSagozillon             fir::unwrapPassByRefType(curValue.getType()))) {
356e508baccSagozillon       mlir::Value idxConst = firOpBuilder.createIntegerConstant(
357e508baccSagozillon           clauseLocation, firOpBuilder.getIndexType(),
358e508baccSagozillon           indices[currentIndicesIdx]);
359e508baccSagozillon       mlir::Type memberTy =
360e508baccSagozillon           recordType.getTypeList().at(indices[currentIndicesIdx]).second;
361e508baccSagozillon       curValue = firOpBuilder.create<fir::CoordinateOp>(
362e508baccSagozillon           clauseLocation, firOpBuilder.getRefType(memberTy), curValue,
363e508baccSagozillon           idxConst);
364e508baccSagozillon 
365e508baccSagozillon       // Skip mapping and the subsequent load if we're the final member or not
366e508baccSagozillon       // a type with a descriptor such as a pointer/allocatable. If we're a
367e508baccSagozillon       // final member, the map will be generated by the processMap call that
368e508baccSagozillon       // invoked this function, and if we're not a type with a descriptor then
369e508baccSagozillon       // we have no need of generating an intermediate map for it, as we only
370e508baccSagozillon       // need to generate a map if a member is a descriptor type (and thus
371e508baccSagozillon       // obscures the members it contains via a pointer in which it's data needs
372e508baccSagozillon       // mapped)
373e508baccSagozillon       if ((currentIndicesIdx == indices.size() - 1) ||
374e508baccSagozillon           !fir::isTypeWithDescriptor(memberTy)) {
375e508baccSagozillon         currentIndicesIdx++;
376e508baccSagozillon         continue;
377e508baccSagozillon       }
378e508baccSagozillon 
379e508baccSagozillon       llvm::SmallVector<int64_t> interimIndices(
380e508baccSagozillon           indices.begin(), std::next(indices.begin(), currentIndicesIdx + 1));
381e508baccSagozillon       // Verify we haven't already created a map for this particular member, by
382e508baccSagozillon       // checking the list of members already mapped for the current parent,
383e508baccSagozillon       // stored in the parentMemberIndices structure
384e508baccSagozillon       if (!parentMemberIndices.isDuplicateMemberMapInfo(interimIndices)) {
385e508baccSagozillon         // Generate bounds operations using the standard lowering utility,
386e508baccSagozillon         // unfortunately this currently does a bit more than just generate
387e508baccSagozillon         // bounds and we discard the other bits. May be useful to extend the
388e508baccSagozillon         // utility to just provide bounds in the future.
389e508baccSagozillon         llvm::SmallVector<mlir::Value> interimBounds;
390e508baccSagozillon         if (i + 1 < objectList.size() &&
391e508baccSagozillon             objectList[i + 1].sym()->IsObjectArray()) {
392e508baccSagozillon           std::stringstream interimFortran;
393e508baccSagozillon           Fortran::lower::gatherDataOperandAddrAndBounds<
394e508baccSagozillon               mlir::omp::MapBoundsOp, mlir::omp::MapBoundsType>(
395e508baccSagozillon               converter, converter.getFirOpBuilder(), semaCtx,
396e508baccSagozillon               converter.getFctCtx(), *objectList[i + 1].sym(),
397e508baccSagozillon               objectList[i + 1].ref(), clauseLocation, interimFortran,
398e508baccSagozillon               interimBounds, treatIndexAsSection);
399e508baccSagozillon         }
400e508baccSagozillon 
401e508baccSagozillon         // Remove all map TO, FROM and TOFROM bits, from the intermediate
402e508baccSagozillon         // allocatable maps, we simply wish to alloc or release them. It may be
403e508baccSagozillon         // safer to just pass OMP_MAP_NONE as the map type, but we may still
404e508baccSagozillon         // need some of the other map types the mapped member utilises, so for
405e508baccSagozillon         // now it's good to keep an eye on this.
406e508baccSagozillon         llvm::omp::OpenMPOffloadMappingFlags interimMapType = mapTypeBits;
407e508baccSagozillon         interimMapType &= ~llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO;
408e508baccSagozillon         interimMapType &= ~llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;
409e508baccSagozillon 
410e508baccSagozillon         // Create a map for the intermediate member and insert it and it's
411e508baccSagozillon         // indices into the parentMemberIndices list to track it.
412e508baccSagozillon         mlir::omp::MapInfoOp mapOp = createMapInfoOp(
413e508baccSagozillon             firOpBuilder, clauseLocation, curValue,
414e508baccSagozillon             /*varPtrPtr=*/mlir::Value{}, asFortran,
415e508baccSagozillon             /*bounds=*/interimBounds,
416e508baccSagozillon             /*members=*/{},
417e508baccSagozillon             /*membersIndex=*/mlir::ArrayAttr{},
418e508baccSagozillon             static_cast<
419e508baccSagozillon                 std::underlying_type_t<llvm::omp::OpenMPOffloadMappingFlags>>(
420e508baccSagozillon                 interimMapType),
421e508baccSagozillon             mlir::omp::VariableCaptureKind::ByRef, curValue.getType());
422e508baccSagozillon 
423e508baccSagozillon         parentMemberIndices.memberPlacementIndices.push_back(interimIndices);
424e508baccSagozillon         parentMemberIndices.memberMap.push_back(mapOp);
425e508baccSagozillon       }
426e508baccSagozillon 
427e508baccSagozillon       // Load the currently accessed member, so we can continue to access
428e508baccSagozillon       // further segments.
429e508baccSagozillon       curValue = firOpBuilder.create<fir::LoadOp>(clauseLocation, curValue);
430e508baccSagozillon       currentIndicesIdx++;
431e508baccSagozillon     }
432e508baccSagozillon   }
433e508baccSagozillon 
434e508baccSagozillon   return curValue;
435e508baccSagozillon }
436e508baccSagozillon 
437e508baccSagozillon static int64_t
4387a66e420SKrzysztof Parzyszek getComponentPlacementInParent(const semantics::Symbol *componentSym) {
4397a66e420SKrzysztof Parzyszek   const auto *derived = componentSym->owner()
440435e850bSAndrew Gozillon                             .derivedTypeSpec()
441435e850bSAndrew Gozillon                             ->typeSymbol()
4427a66e420SKrzysztof Parzyszek                             .detailsIf<semantics::DerivedTypeDetails>();
443435e850bSAndrew Gozillon   assert(derived &&
444435e850bSAndrew Gozillon          "expected derived type details when processing component symbol");
445435e850bSAndrew Gozillon   for (auto [placement, name] : llvm::enumerate(derived->componentNames()))
446435e850bSAndrew Gozillon     if (name == componentSym->name())
447435e850bSAndrew Gozillon       return placement;
448435e850bSAndrew Gozillon   return -1;
449435e850bSAndrew Gozillon }
450435e850bSAndrew Gozillon 
451435e850bSAndrew Gozillon static std::optional<Object>
452435e850bSAndrew Gozillon getComponentObject(std::optional<Object> object,
4537a66e420SKrzysztof Parzyszek                    semantics::SemanticsContext &semaCtx) {
454435e850bSAndrew Gozillon   if (!object)
455435e850bSAndrew Gozillon     return std::nullopt;
456435e850bSAndrew Gozillon 
457435e850bSAndrew Gozillon   auto ref = evaluate::ExtractDataRef(*object.value().ref());
458435e850bSAndrew Gozillon   if (!ref)
459435e850bSAndrew Gozillon     return std::nullopt;
460435e850bSAndrew Gozillon 
461435e850bSAndrew Gozillon   if (std::holds_alternative<evaluate::Component>(ref->u))
462435e850bSAndrew Gozillon     return object;
463435e850bSAndrew Gozillon 
464435e850bSAndrew Gozillon   auto baseObj = getBaseObject(object.value(), semaCtx);
465435e850bSAndrew Gozillon   if (!baseObj)
466435e850bSAndrew Gozillon     return std::nullopt;
467435e850bSAndrew Gozillon 
468435e850bSAndrew Gozillon   return getComponentObject(baseObj.value(), semaCtx);
469435e850bSAndrew Gozillon }
470435e850bSAndrew Gozillon 
471e508baccSagozillon void generateMemberPlacementIndices(const Object &object,
472e508baccSagozillon                                     llvm::SmallVectorImpl<int64_t> &indices,
4737a66e420SKrzysztof Parzyszek                                     semantics::SemanticsContext &semaCtx) {
474e508baccSagozillon   assert(indices.empty() && "indices vector passed to "
475e508baccSagozillon                             "generateMemberPlacementIndices should be empty");
476435e850bSAndrew Gozillon   auto compObj = getComponentObject(object, semaCtx);
477e508baccSagozillon 
478435e850bSAndrew Gozillon   while (compObj) {
479e508baccSagozillon     int64_t index = getComponentPlacementInParent(compObj->sym());
480e508baccSagozillon     assert(
481e508baccSagozillon         index >= 0 &&
482e508baccSagozillon         "unexpected index value returned from getComponentPlacementInParent");
483e508baccSagozillon     indices.push_back(index);
484435e850bSAndrew Gozillon     compObj =
485435e850bSAndrew Gozillon         getComponentObject(getBaseObject(compObj.value(), semaCtx), semaCtx);
486435e850bSAndrew Gozillon   }
487435e850bSAndrew Gozillon 
488e508baccSagozillon   indices = llvm::SmallVector<int64_t>{llvm::reverse(indices)};
489435e850bSAndrew Gozillon }
490435e850bSAndrew Gozillon 
491e508baccSagozillon void OmpMapParentAndMemberData::addChildIndexAndMapToParent(
492e508baccSagozillon     const omp::Object &object, mlir::omp::MapInfoOp &mapOp,
493e508baccSagozillon     semantics::SemanticsContext &semaCtx) {
494e508baccSagozillon   llvm::SmallVector<int64_t> indices;
495435e850bSAndrew Gozillon   generateMemberPlacementIndices(object, indices, semaCtx);
496e508baccSagozillon   memberPlacementIndices.push_back(indices);
497e508baccSagozillon   memberMap.push_back(mapOp);
498435e850bSAndrew Gozillon }
499435e850bSAndrew Gozillon 
500e508baccSagozillon bool isMemberOrParentAllocatableOrPointer(
501e508baccSagozillon     const Object &object, semantics::SemanticsContext &semaCtx) {
502e508baccSagozillon   if (semantics::IsAllocatableOrObjectPointer(object.sym()))
503e508baccSagozillon     return true;
504435e850bSAndrew Gozillon 
505e508baccSagozillon   auto compObj = getBaseObject(object, semaCtx);
506e508baccSagozillon   while (compObj) {
507e508baccSagozillon     if (semantics::IsAllocatableOrObjectPointer(compObj.value().sym()))
508e508baccSagozillon       return true;
509e508baccSagozillon     compObj = getBaseObject(compObj.value(), semaCtx);
510435e850bSAndrew Gozillon   }
511435e850bSAndrew Gozillon 
512e508baccSagozillon   return false;
513435e850bSAndrew Gozillon }
514435e850bSAndrew Gozillon 
515435e850bSAndrew Gozillon void insertChildMapInfoIntoParent(
516e508baccSagozillon     lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,
517e508baccSagozillon     lower::StatementContext &stmtCtx,
518e508baccSagozillon     std::map<Object, OmpMapParentAndMemberData> &parentMemberIndices,
519435e850bSAndrew Gozillon     llvm::SmallVectorImpl<mlir::Value> &mapOperands,
52088478a89SSergio Afonso     llvm::SmallVectorImpl<const semantics::Symbol *> &mapSyms) {
521e508baccSagozillon   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
522435e850bSAndrew Gozillon   for (auto indices : parentMemberIndices) {
523e508baccSagozillon     auto *parentIter =
524e508baccSagozillon         llvm::find_if(mapSyms, [&indices](const semantics::Symbol *v) {
525e508baccSagozillon           return v == indices.first.sym();
526e508baccSagozillon         });
527e508baccSagozillon     if (parentIter != mapSyms.end()) {
528435e850bSAndrew Gozillon       auto mapOp = llvm::cast<mlir::omp::MapInfoOp>(
529e508baccSagozillon           mapOperands[std::distance(mapSyms.begin(), parentIter)]
530e508baccSagozillon               .getDefiningOp());
531435e850bSAndrew Gozillon 
532435e850bSAndrew Gozillon       // NOTE: To maintain appropriate SSA ordering, we move the parent map
533435e850bSAndrew Gozillon       // which will now have references to its children after the last
534435e850bSAndrew Gozillon       // of its members to be generated. This is necessary when a user
535435e850bSAndrew Gozillon       // has defined a series of parent and children maps where the parent
536435e850bSAndrew Gozillon       // precedes the children. An alternative, may be to do
537435e850bSAndrew Gozillon       // delayed generation of map info operations from the clauses and
538e508baccSagozillon       // organize them first before generation. Or to use the
539e508baccSagozillon       // topologicalSort utility which will enforce a stronger SSA
540e508baccSagozillon       // dominance ordering at the cost of efficiency/time.
541e508baccSagozillon       mapOp->moveAfter(indices.second.memberMap.back());
542435e850bSAndrew Gozillon 
543e508baccSagozillon       for (mlir::omp::MapInfoOp memberMap : indices.second.memberMap)
544e508baccSagozillon         mapOp.getMembersMutable().append(memberMap.getResult());
545435e850bSAndrew Gozillon 
546e508baccSagozillon       mapOp.setMembersIndexAttr(firOpBuilder.create2DI64ArrayAttr(
547e508baccSagozillon           indices.second.memberPlacementIndices));
548435e850bSAndrew Gozillon     } else {
549435e850bSAndrew Gozillon       // NOTE: We take the map type of the first child, this may not
550435e850bSAndrew Gozillon       // be the correct thing to do, however, we shall see. For the moment
551435e850bSAndrew Gozillon       // it allows this to work with enter and exit without causing MLIR
552435e850bSAndrew Gozillon       // verification issues. The more appropriate thing may be to take
553435e850bSAndrew Gozillon       // the "main" map type clause from the directive being used.
554e508baccSagozillon       uint64_t mapType = indices.second.memberMap[0].getMapType().value_or(0);
555435e850bSAndrew Gozillon 
556435e850bSAndrew Gozillon       llvm::SmallVector<mlir::Value> members;
557e508baccSagozillon       members.reserve(indices.second.memberMap.size());
558e508baccSagozillon       for (mlir::omp::MapInfoOp memberMap : indices.second.memberMap)
559e508baccSagozillon         members.push_back(memberMap.getResult());
560435e850bSAndrew Gozillon 
561e508baccSagozillon       // Create parent to emplace and bind members
562e508baccSagozillon       llvm::SmallVector<mlir::Value> bounds;
563e508baccSagozillon       std::stringstream asFortran;
564*662133a2SjeanPerier       fir::factory::AddrAndBoundsInfo info =
565e508baccSagozillon           lower::gatherDataOperandAddrAndBounds<mlir::omp::MapBoundsOp,
566e508baccSagozillon                                                 mlir::omp::MapBoundsType>(
567e508baccSagozillon               converter, firOpBuilder, semaCtx, converter.getFctCtx(),
568e508baccSagozillon               *indices.first.sym(), indices.first.ref(),
569e508baccSagozillon               converter.getCurrentLocation(), asFortran, bounds,
570e508baccSagozillon               treatIndexAsSection);
571e508baccSagozillon 
572e508baccSagozillon       mlir::omp::MapInfoOp mapOp = createMapInfoOp(
573e508baccSagozillon           firOpBuilder, info.rawInput.getLoc(), info.rawInput,
574e508baccSagozillon           /*varPtrPtr=*/mlir::Value(), asFortran.str(), bounds, members,
575e508baccSagozillon           firOpBuilder.create2DI64ArrayAttr(
576e508baccSagozillon               indices.second.memberPlacementIndices),
577e508baccSagozillon           mapType, mlir::omp::VariableCaptureKind::ByRef,
578e508baccSagozillon           info.rawInput.getType(),
579435e850bSAndrew Gozillon           /*partialMap=*/true);
580435e850bSAndrew Gozillon 
581435e850bSAndrew Gozillon       mapOperands.push_back(mapOp);
582e508baccSagozillon       mapSyms.push_back(indices.first.sym());
583435e850bSAndrew Gozillon     }
584435e850bSAndrew Gozillon   }
585435e850bSAndrew Gozillon }
586435e850bSAndrew Gozillon 
587f9824439SKrzysztof Parzyszek void lastprivateModifierNotSupported(const omp::clause::Lastprivate &lastp,
588f9824439SKrzysztof Parzyszek                                      mlir::Location loc) {
589f9824439SKrzysztof Parzyszek   using Lastprivate = omp::clause::Lastprivate;
590f9824439SKrzysztof Parzyszek   auto &maybeMod =
591f9824439SKrzysztof Parzyszek       std::get<std::optional<Lastprivate::LastprivateModifier>>(lastp.t);
592f9824439SKrzysztof Parzyszek   if (maybeMod) {
593f9824439SKrzysztof Parzyszek     assert(*maybeMod == Lastprivate::LastprivateModifier::Conditional &&
594f9824439SKrzysztof Parzyszek            "Unexpected lastprivate modifier");
595f9824439SKrzysztof Parzyszek     TODO(loc, "lastprivate clause with CONDITIONAL modifier");
596f9824439SKrzysztof Parzyszek   }
597f9824439SKrzysztof Parzyszek }
598f9824439SKrzysztof Parzyszek 
5994d4af15cSKareem Ergawy } // namespace omp
6004d4af15cSKareem Ergawy } // namespace lower
6014d4af15cSKareem Ergawy } // namespace Fortran
602