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