brintos

brintos / llvm-project-archived public Read only

0
0
Text · 39.4 KiB · ccac643 Raw
920 lines · cpp
1//===-- Utils..cpp ----------------------------------------------*- 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-exception6//7//===----------------------------------------------------------------------===//8//9// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/10//11//===----------------------------------------------------------------------===//12 13#include "Utils.h"14 15#include "ClauseFinder.h"16#include "flang/Evaluate/fold.h"17#include "flang/Evaluate/tools.h"18#include <flang/Lower/AbstractConverter.h>19#include <flang/Lower/ConvertType.h>20#include <flang/Lower/DirectivesCommon.h>21#include <flang/Lower/OpenMP/Clauses.h>22#include <flang/Lower/PFTBuilder.h>23#include <flang/Lower/Support/PrivateReductionUtils.h>24#include <flang/Optimizer/Builder/BoxValue.h>25#include <flang/Optimizer/Builder/FIRBuilder.h>26#include <flang/Optimizer/Builder/Todo.h>27#include <flang/Optimizer/HLFIR/HLFIROps.h>28#include <flang/Parser/openmp-utils.h>29#include <flang/Parser/parse-tree.h>30#include <flang/Parser/tools.h>31#include <flang/Semantics/tools.h>32#include <flang/Semantics/type.h>33#include <flang/Utils/OpenMP.h>34#include <llvm/ADT/SmallPtrSet.h>35#include <llvm/ADT/StringRef.h>36#include <llvm/Support/CommandLine.h>37 38#include <functional>39#include <iterator>40 41template <typename T>42Fortran::semantics::MaybeIntExpr43EvaluateIntExpr(Fortran::semantics::SemanticsContext &context, const T &expr) {44  if (Fortran::semantics::MaybeExpr maybeExpr{45          Fold(context.foldingContext(), AnalyzeExpr(context, expr))}) {46    if (auto *intExpr{47            Fortran::evaluate::UnwrapExpr<Fortran::semantics::SomeIntExpr>(48                *maybeExpr)}) {49      return std::move(*intExpr);50    }51  }52  return std::nullopt;53}54 55template <typename T>56std::optional<std::int64_t>57EvaluateInt64(Fortran::semantics::SemanticsContext &context, const T &expr) {58  return Fortran::evaluate::ToInt64(EvaluateIntExpr(context, expr));59}60 61llvm::cl::opt<bool> treatIndexAsSection(62    "openmp-treat-index-as-section",63    llvm::cl::desc("In the OpenMP data clauses treat `a(N)` as `a(N:N)`."),64    llvm::cl::init(true));65 66namespace Fortran {67namespace lower {68namespace omp {69 70mlir::FlatSymbolRefAttr getOrGenImplicitDefaultDeclareMapper(71    lower::AbstractConverter &converter, mlir::Location loc,72    fir::RecordType recordType, llvm::StringRef mapperNameStr) {73  if (mapperNameStr.empty())74    return {};75 76  if (converter.getModuleOp().lookupSymbol(mapperNameStr))77    return mlir::FlatSymbolRefAttr::get(&converter.getMLIRContext(),78                                        mapperNameStr);79 80  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();81  mlir::OpBuilder::InsertionGuard guard(firOpBuilder);82 83  firOpBuilder.setInsertionPointToStart(converter.getModuleOp().getBody());84  auto declMapperOp = mlir::omp::DeclareMapperOp::create(85      firOpBuilder, loc, mapperNameStr, recordType);86  auto &region = declMapperOp.getRegion();87  firOpBuilder.createBlock(&region);88  auto mapperArg = region.addArgument(firOpBuilder.getRefType(recordType), loc);89 90  auto declareOp = hlfir::DeclareOp::create(firOpBuilder, loc, mapperArg,91                                            /*uniq_name=*/"");92 93  const auto genBoundsOps = [&](mlir::Value mapVal,94                                llvm::SmallVectorImpl<mlir::Value> &bounds) {95    fir::ExtendedValue extVal =96        hlfir::translateToExtendedValue(mapVal.getLoc(), firOpBuilder,97                                        hlfir::Entity{mapVal},98                                        /*contiguousHint=*/true)99            .first;100    fir::factory::AddrAndBoundsInfo info = fir::factory::getDataOperandBaseAddr(101        firOpBuilder, mapVal, /*isOptional=*/false, mapVal.getLoc());102    bounds = fir::factory::genImplicitBoundsOps<mlir::omp::MapBoundsOp,103                                                mlir::omp::MapBoundsType>(104        firOpBuilder, info, extVal,105        /*dataExvIsAssumedSize=*/false, mapVal.getLoc());106  };107 108  const auto getFieldRef = [&](mlir::Value rec, llvm::StringRef fieldName,109                               mlir::Type fieldTy, mlir::Type recType) {110    mlir::Value field = fir::FieldIndexOp::create(111        firOpBuilder, loc, fir::FieldType::get(recType.getContext()), fieldName,112        recType, fir::getTypeParams(rec));113    return fir::CoordinateOp::create(114        firOpBuilder, loc, firOpBuilder.getRefType(fieldTy), rec, field);115  };116 117  llvm::SmallVector<mlir::Value> clauseMapVars;118  llvm::SmallVector<llvm::SmallVector<int64_t>> memberPlacementIndices;119  llvm::SmallVector<mlir::Value> memberMapOps;120 121  mlir::omp::ClauseMapFlags mapFlag = mlir::omp::ClauseMapFlags::to |122                                      mlir::omp::ClauseMapFlags::from |123                                      mlir::omp::ClauseMapFlags::implicit;124  mlir::omp::VariableCaptureKind captureKind =125      mlir::omp::VariableCaptureKind::ByRef;126 127  for (const auto &entry : llvm::enumerate(recordType.getTypeList())) {128    const auto &memberName = entry.value().first;129    const auto &memberType = entry.value().second;130    mlir::FlatSymbolRefAttr mapperId;131    if (auto recType = mlir::dyn_cast<fir::RecordType>(132            fir::getFortranElementType(memberType))) {133      std::string mapperIdName =134          recType.getName().str() + llvm::omp::OmpDefaultMapperName;135      if (auto *sym = converter.getCurrentScope().FindSymbol(mapperIdName))136        mapperIdName = converter.mangleName(mapperIdName, sym->owner());137      else if (auto *memberSym =138                   converter.getCurrentScope().FindSymbol(memberName))139        mapperIdName = converter.mangleName(mapperIdName, memberSym->owner());140 141      mapperId = getOrGenImplicitDefaultDeclareMapper(converter, loc, recType,142                                                      mapperIdName);143    }144 145    auto ref =146        getFieldRef(declareOp.getBase(), memberName, memberType, recordType);147    llvm::SmallVector<mlir::Value> bounds;148    genBoundsOps(ref, bounds);149    mlir::Value mapOp = Fortran::utils::openmp::createMapInfoOp(150        firOpBuilder, loc, ref, /*varPtrPtr=*/mlir::Value{}, /*name=*/"",151        bounds,152        /*members=*/{},153        /*membersIndex=*/mlir::ArrayAttr{}, mapFlag, captureKind, ref.getType(),154        /*partialMap=*/false, mapperId);155    memberMapOps.emplace_back(mapOp);156    memberPlacementIndices.emplace_back(157        llvm::SmallVector<int64_t>{(int64_t)entry.index()});158  }159 160  llvm::SmallVector<mlir::Value> bounds;161  genBoundsOps(declareOp.getOriginalBase(), bounds);162  mlir::omp::ClauseMapFlags parentMapFlag = mlir::omp::ClauseMapFlags::implicit;163  mlir::omp::MapInfoOp mapOp = Fortran::utils::openmp::createMapInfoOp(164      firOpBuilder, loc, declareOp.getOriginalBase(),165      /*varPtrPtr=*/mlir::Value(), /*name=*/"", bounds, memberMapOps,166      firOpBuilder.create2DI64ArrayAttr(memberPlacementIndices), parentMapFlag,167      captureKind, declareOp.getType(0),168      /*partialMap=*/true);169 170  clauseMapVars.emplace_back(mapOp);171  mlir::omp::DeclareMapperInfoOp::create(firOpBuilder, loc, clauseMapVars);172  return mlir::FlatSymbolRefAttr::get(&converter.getMLIRContext(),173                                      mapperNameStr);174}175 176bool requiresImplicitDefaultDeclareMapper(177    const semantics::DerivedTypeSpec &typeSpec) {178  // ISO C interoperable types (e.g., c_ptr, c_funptr) must always have implicit179  // default mappers available so that OpenMP offloading can correctly map them.180  if (semantics::IsIsoCType(&typeSpec))181    return true;182 183  llvm::SmallPtrSet<const semantics::DerivedTypeSpec *, 8> visited;184 185  std::function<bool(const semantics::DerivedTypeSpec &)> requiresMapper =186      [&](const semantics::DerivedTypeSpec &spec) -> bool {187    if (!visited.insert(&spec).second)188      return false;189 190    semantics::DirectComponentIterator directComponents{spec};191    for (const semantics::Symbol &component : directComponents) {192      if (semantics::IsAllocatableOrPointer(component))193        return true;194 195      if (const semantics::DeclTypeSpec *declType = component.GetType())196        if (const auto *nested = declType->AsDerived())197          if (requiresMapper(*nested))198            return true;199    }200    return false;201  };202 203  return requiresMapper(typeSpec);204}205 206int64_t getCollapseValue(const List<Clause> &clauses) {207  auto iter = llvm::find_if(clauses, [](const Clause &clause) {208    return clause.id == llvm::omp::Clause::OMPC_collapse;209  });210  if (iter != clauses.end()) {211    const auto &collapse = std::get<clause::Collapse>(iter->u);212    return evaluate::ToInt64(collapse.v).value();213  }214  return 1;215}216 217void genObjectList(const ObjectList &objects,218                   lower::AbstractConverter &converter,219                   llvm::SmallVectorImpl<mlir::Value> &operands) {220  for (const Object &object : objects) {221    const semantics::Symbol *sym = object.sym();222    assert(sym && "Expected Symbol");223    if (mlir::Value variable = converter.getSymbolAddress(*sym)) {224      operands.push_back(variable);225    } else if (const auto *details =226                   sym->detailsIf<semantics::HostAssocDetails>()) {227      operands.push_back(converter.getSymbolAddress(details->symbol()));228      converter.copySymbolBinding(details->symbol(), *sym);229    }230  }231}232 233mlir::Type getLoopVarType(lower::AbstractConverter &converter,234                          std::size_t loopVarTypeSize) {235  // OpenMP runtime requires 32-bit or 64-bit loop variables.236  loopVarTypeSize = loopVarTypeSize * 8;237  if (loopVarTypeSize < 32) {238    loopVarTypeSize = 32;239  } else if (loopVarTypeSize > 64) {240    loopVarTypeSize = 64;241    mlir::emitWarning(converter.getCurrentLocation(),242                      "OpenMP loop iteration variable cannot have more than 64 "243                      "bits size and will be narrowed into 64 bits.");244  }245  assert((loopVarTypeSize == 32 || loopVarTypeSize == 64) &&246         "OpenMP loop iteration variable size must be transformed into 32-bit "247         "or 64-bit");248  return converter.getFirOpBuilder().getIntegerType(loopVarTypeSize);249}250 251semantics::Symbol *252getIterationVariableSymbol(const lower::pft::Evaluation &eval) {253  return eval.visit(common::visitors{254      [&](const parser::DoConstruct &doLoop) {255        if (const auto &maybeCtrl = doLoop.GetLoopControl()) {256          using LoopControl = parser::LoopControl;257          if (auto *bounds = std::get_if<LoopControl::Bounds>(&maybeCtrl->u)) {258            static_assert(std::is_same_v<decltype(bounds->name),259                                         parser::Scalar<parser::Name>>);260            return bounds->name.thing.symbol;261          }262        }263        return static_cast<semantics::Symbol *>(nullptr);264      },265      [](auto &&) { return static_cast<semantics::Symbol *>(nullptr); },266  });267}268 269void gatherFuncAndVarSyms(270    const ObjectList &objects, mlir::omp::DeclareTargetCaptureClause clause,271    llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &symbolAndClause,272    bool automap) {273  for (const Object &object : objects)274    symbolAndClause.emplace_back(clause, *object.sym(), automap);275}276 277// This function gathers the individual omp::Object's that make up a278// larger omp::Object symbol.279//280// For example, provided the larger symbol: "parent%child%member", this281// function breaks it up into its constituent components ("parent",282// "child", "member"), so we can access each individual component and283// introspect details. Important to note is this function breaks it up from284// RHS to LHS ("member" to "parent") and then we reverse it so that the285// returned omp::ObjectList is LHS to RHS, with the "parent" at the286// beginning.287omp::ObjectList gatherObjectsOf(omp::Object derivedTypeMember,288                                semantics::SemanticsContext &semaCtx) {289  omp::ObjectList objList;290  std::optional<omp::Object> baseObj = derivedTypeMember;291  while (baseObj.has_value()) {292    objList.push_back(baseObj.value());293    baseObj = getBaseObject(baseObj.value(), semaCtx);294  }295  return omp::ObjectList{llvm::reverse(objList)};296}297 298// This function generates a series of indices from a provided omp::Object,299// that devolves to an ArrayRef symbol, e.g. "array(2,3,4)", this function300// would generate a series of indices of "[1][2][3]" for the above example,301// offsetting by -1 to account for the non-zero fortran indexes.302//303// These indices can then be provided to a coordinate operation or other304// GEP-like operation to access the relevant positional member of the305// array.306//307// It is of note that the function only supports subscript integers currently308// and not Triplets i.e. Array(1:2:3).309static void generateArrayIndices(lower::AbstractConverter &converter,310                                 fir::FirOpBuilder &firOpBuilder,311                                 lower::StatementContext &stmtCtx,312                                 mlir::Location clauseLocation,313                                 llvm::SmallVectorImpl<mlir::Value> &indices,314                                 omp::Object object) {315  auto maybeRef = evaluate::ExtractDataRef(*object.ref());316  if (!maybeRef)317    return;318 319  auto *arr = std::get_if<evaluate::ArrayRef>(&maybeRef->u);320  if (!arr)321    return;322 323  for (auto v : arr->subscript()) {324    if (std::holds_alternative<Triplet>(v.u))325      TODO(clauseLocation, "Triplet indexing in map clause is unsupported");326    auto expr = std::get<Fortran::evaluate::IndirectSubscriptIntegerExpr>(v.u);327    mlir::Value subscript =328        fir::getBase(converter.genExprValue(toEvExpr(expr.value()), stmtCtx));329    indices.push_back(firOpBuilder.createConvert(330        clauseLocation, firOpBuilder.getIndexType(), subscript));331  }332}333 334/// When mapping members of derived types, there is a chance that one of the335/// members along the way to a mapped member is an descriptor. In which case336/// we have to make sure we generate a map for those along the way otherwise337/// we will be missing a chunk of data required to actually map the member338/// type to device. This function effectively generates these maps and the339/// appropriate data accesses required to generate these maps. It will avoid340/// creating duplicate maps, as duplicates are just as bad as unmapped341/// descriptor data in a lot of cases for the runtime (and unnecessary342/// data movement should be avoided where possible).343///344/// As an example for the following mapping:345///346/// type :: vertexes347///     integer(4), allocatable :: vertexx(:)348///     integer(4), allocatable :: vertexy(:)349/// end type vertexes350///351/// type :: dtype352///     real(4) :: i353///     type(vertexes), allocatable :: vertexes(:)354/// end type dtype355///356/// type(dtype), allocatable :: alloca_dtype357///358/// !$omp target map(tofrom: alloca_dtype%vertexes(N1)%vertexx)359///360/// The below HLFIR/FIR is generated (trimmed for conciseness):361///362/// On the first iteration we index into the record type alloca_dtype363/// to access "vertexes", we then generate a map for this descriptor364/// alongside bounds to indicate we only need the 1 member, rather than365/// the whole array block in this case (In theory we could map its366/// entirety at the cost of data transfer bandwidth).367///368/// %13:2 = hlfir.declare ... "alloca_dtype" ...369/// %39 = fir.load %13#0 : ...370/// %40 = fir.coordinate_of %39, %c1 : ...371/// %51 = omp.map.info var_ptr(%40 : ...) map_clauses(to) capture(ByRef) ...372/// %52 = fir.load %40 : ...373///374/// Second iteration generating access to "vertexes(N1) utilising the N1 index375/// %53 = load N1 ...376/// %54 = fir.convert %53 : (i32) -> i64377/// %55 = fir.convert %54 : (i64) -> index378/// %56 = arith.subi %55, %c1 : index379/// %57 = fir.coordinate_of %52, %56 : ...380///381/// Still in the second iteration we access the allocatable member "vertexx",382/// we return %58 from the function and provide it to the final and "main"383/// map of processMap (generated by the record type segment of the below384/// function), if this were not the final symbol in the list, i.e. we accessed385/// a member below vertexx, we would have generated the map below as we did in386/// the first iteration and then continue to generate further coordinates to387/// access further components as required.388///389/// %58 = fir.coordinate_of %57, %c0 : ...390/// %61 = omp.map.info var_ptr(%58 : ...) map_clauses(to) capture(ByRef) ...391///392/// Parent mapping containing prior generated mapped members, generated at393/// a later step but here to showcase the "end" result394///395/// omp.map.info var_ptr(%13#1 : ...) map_clauses(to) capture(ByRef)396///   members(%50, %61 : [0, 1, 0], [0, 1, 0] : ...397///398/// \param objectList - The list of omp::Object symbol data for each parent399///  to the mapped member (also includes the mapped member), generated via400///  gatherObjectsOf.401/// \param indices - List of index data associated with the mapped member402///   symbol, which identifies the placement of the member in its parent,403///   this helps generate the appropriate member accesses. These indices404///   can be generated via generateMemberPlacementIndices.405/// \param asFortran - A string generated from the mapped variable to be406///   associated with the main map, generally (but not restricted to)407///   generated via gatherDataOperandAddrAndBounds or other408///   DirectiveCommons.hpp utilities.409/// \param mapTypeBits - The map flags that will be associated with the410///   generated maps, minus alterations of the TO and FROM bits for the411///   intermediate components to prevent accidental overwriting on device412///   write back.413mlir::Value createParentSymAndGenIntermediateMaps(414    mlir::Location clauseLocation, lower::AbstractConverter &converter,415    semantics::SemanticsContext &semaCtx, lower::StatementContext &stmtCtx,416    omp::ObjectList &objectList, llvm::SmallVectorImpl<int64_t> &indices,417    OmpMapParentAndMemberData &parentMemberIndices, llvm::StringRef asFortran,418    mlir::omp::ClauseMapFlags mapTypeBits) {419  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();420 421  /// Checks if an omp::Object is an array expression with a subscript, e.g.422  /// array(1,2).423  auto isArrayExprWithSubscript = [](omp::Object obj) {424    if (auto maybeRef = evaluate::ExtractDataRef(obj.ref())) {425      evaluate::DataRef ref = *maybeRef;426      if (auto *arr = std::get_if<evaluate::ArrayRef>(&ref.u))427        return !arr->subscript().empty();428    }429    return false;430  };431 432  // Generate the access to the original parent base address.433  fir::factory::AddrAndBoundsInfo parentBaseAddr =434      lower::getDataOperandBaseAddr(converter, firOpBuilder,435                                    *objectList[0].sym(), clauseLocation);436  mlir::Value curValue = parentBaseAddr.addr;437 438  // Iterate over all objects in the objectList, this should consist of all439  // record types between the parent and the member being mapped (including440  // the parent). The object list may also contain array objects as well,441  // this can occur when specifying bounds or a specific element access442  // within a member map, we skip these.443  size_t currentIndicesIdx = 0;444  for (size_t i = 0; i < objectList.size(); ++i) {445    // If we encounter a sequence type, i.e. an array, we must generate the446    // correct coordinate operation to index into the array to proceed further,447    // this is only relevant in cases where we encounter subscripts currently.448    //449    // For example in the following case:450    //451    //   map(tofrom: array_dtype(4)%internal_dtypes(3)%float_elements(4))452    //453    // We must generate coordinate operation accesses for each subscript454    // we encounter.455    if (fir::SequenceType arrType = mlir::dyn_cast<fir::SequenceType>(456            fir::unwrapPassByRefType(curValue.getType()))) {457      if (isArrayExprWithSubscript(objectList[i])) {458        llvm::SmallVector<mlir::Value> subscriptIndices;459        generateArrayIndices(converter, firOpBuilder, stmtCtx, clauseLocation,460                             subscriptIndices, objectList[i]);461        assert(!subscriptIndices.empty() &&462               "missing expected indices for map clause");463        if (auto boxTy = llvm::dyn_cast<fir::BaseBoxType>(curValue.getType())) {464          // To accommodate indexing into box types of all dimensions including465          // negative dimensions we have to take into consideration the lower466          // bounds and extents of the data (stored in the box) and convey it467          // to the ArrayCoorOp so that it can appropriately access the element468          // utilising the subscript we provide and the runtime sizes stored in469          // the Box. To do so we need to generate a ShapeShiftOp which combines470          // both the lb (ShiftOp) and extent (ShapeOp) of the Box, giving the471          // ArrayCoorOp the spatial information it needs to calculate the472          // underlying address.473          mlir::Value shapeShift = Fortran::lower::getShapeShift(474              firOpBuilder, clauseLocation, curValue);475          auto addrOp =476              fir::BoxAddrOp::create(firOpBuilder, clauseLocation, curValue);477          curValue = fir::ArrayCoorOp::create(478              firOpBuilder, clauseLocation,479              firOpBuilder.getRefType(arrType.getEleTy()), addrOp, shapeShift,480              /*slice=*/mlir::Value{}, subscriptIndices,481              /*typeparms=*/mlir::ValueRange{});482        } else {483          // We're required to negate by one in the non-Box case as I believe484          // we do not have the shape generated from the dimensions to help485          // adjust the indexing.486          // TODO/FIXME: This may need adjusted to support bounds of unusual487          // dimensions, if that's the case then it is likely best to fold this488          // branch into the above.489          mlir::Value one = firOpBuilder.createIntegerConstant(490              clauseLocation, firOpBuilder.getIndexType(), 1);491          for (auto &v : subscriptIndices)492            v = mlir::arith::SubIOp::create(firOpBuilder, clauseLocation, v,493                                            one);494          curValue = fir::CoordinateOp::create(495              firOpBuilder, clauseLocation,496              firOpBuilder.getRefType(arrType.getEleTy()), curValue,497              subscriptIndices);498        }499      }500    }501 502    // If we encounter a record type, we must access the subsequent member503    // by indexing into it and creating a coordinate operation to do so, we504    // utilise the index information generated previously and passed in to505    // work out the correct member to access and the corresponding member506    // type.507    if (fir::RecordType recordType = mlir::dyn_cast<fir::RecordType>(508            fir::unwrapPassByRefType(curValue.getType()))) {509      fir::IntOrValue idxConst = mlir::IntegerAttr::get(510          firOpBuilder.getI32Type(), indices[currentIndicesIdx]);511      mlir::Type memberTy = recordType.getType(indices[currentIndicesIdx]);512      curValue = fir::CoordinateOp::create(513          firOpBuilder, clauseLocation, firOpBuilder.getRefType(memberTy),514          curValue, llvm::SmallVector<fir::IntOrValue, 1>{idxConst});515 516      // If we're a final member, the map will be generated by the processMap517      // call that invoked this function.518      if (currentIndicesIdx == indices.size() - 1)519        break;520 521      // Skip mapping and the subsequent load if we're not522      // a type with a descriptor such as a pointer/allocatable. If we're not a523      // type with a descriptor then we have no need of generating an524      // intermediate map for it, as we only need to generate a map if a member525      // is a descriptor type (and thus obscures the members it contains via a526      // pointer in which it's data needs mapped).527      if (!fir::isTypeWithDescriptor(memberTy)) {528        currentIndicesIdx++;529        continue;530      }531 532      llvm::SmallVector<int64_t> interimIndices(533          indices.begin(), std::next(indices.begin(), currentIndicesIdx + 1));534      // Verify we haven't already created a map for this particular member, by535      // checking the list of members already mapped for the current parent,536      // stored in the parentMemberIndices structure537      if (!parentMemberIndices.isDuplicateMemberMapInfo(interimIndices)) {538        // Generate bounds operations using the standard lowering utility,539        // unfortunately this currently does a bit more than just generate540        // bounds and we discard the other bits. May be useful to extend the541        // utility to just provide bounds in the future.542        llvm::SmallVector<mlir::Value> interimBounds;543        if (i + 1 < objectList.size() &&544            objectList[i + 1].sym()->IsObjectArray()) {545          std::stringstream interimFortran;546          Fortran::lower::gatherDataOperandAddrAndBounds<547              mlir::omp::MapBoundsOp, mlir::omp::MapBoundsType>(548              converter, converter.getFirOpBuilder(), semaCtx,549              converter.getFctCtx(), *objectList[i + 1].sym(),550              objectList[i + 1].ref(), clauseLocation, interimFortran,551              interimBounds, treatIndexAsSection);552        }553 554        // Remove all map-type bits (e.g. TO, FROM, etc.) from the intermediate555        // allocatable maps, as we simply wish to alloc or release them. It may556        // be safer to just pass OMP_MAP_NONE as the map type, but we may still557        // need some of the other map types the mapped member utilises, so for558        // now it's good to keep an eye on this.559        mlir::omp::ClauseMapFlags interimMapType = mapTypeBits;560        interimMapType &= ~mlir::omp::ClauseMapFlags::to;561        interimMapType &= ~mlir::omp::ClauseMapFlags::from;562        interimMapType &= ~mlir::omp::ClauseMapFlags::return_param;563 564        // Create a map for the intermediate member and insert it and it's565        // indices into the parentMemberIndices list to track it.566        mlir::omp::MapInfoOp mapOp = utils::openmp::createMapInfoOp(567            firOpBuilder, clauseLocation, curValue,568            /*varPtrPtr=*/mlir::Value{}, asFortran,569            /*bounds=*/interimBounds,570            /*members=*/{},571            /*membersIndex=*/mlir::ArrayAttr{}, interimMapType,572            mlir::omp::VariableCaptureKind::ByRef, curValue.getType());573 574        parentMemberIndices.memberPlacementIndices.push_back(interimIndices);575        parentMemberIndices.memberMap.push_back(mapOp);576      }577 578      // Load the currently accessed member, so we can continue to access579      // further segments.580      curValue = fir::LoadOp::create(firOpBuilder, clauseLocation, curValue);581      currentIndicesIdx++;582    }583  }584  return curValue;585}586 587static int64_t588getComponentPlacementInParent(const semantics::Symbol *componentSym) {589  const auto *derived = componentSym->owner()590                            .derivedTypeSpec()591                            ->typeSymbol()592                            .detailsIf<semantics::DerivedTypeDetails>();593  assert(derived &&594         "expected derived type details when processing component symbol");595  for (auto [placement, name] : llvm::enumerate(derived->componentNames()))596    if (name == componentSym->name())597      return placement;598  return -1;599}600 601static std::optional<Object>602getComponentObject(std::optional<Object> object,603                   semantics::SemanticsContext &semaCtx) {604  if (!object)605    return std::nullopt;606 607  auto ref = evaluate::ExtractDataRef(object.value().ref());608  if (!ref)609    return std::nullopt;610 611  if (std::holds_alternative<evaluate::Component>(ref->u))612    return object;613 614  auto baseObj = getBaseObject(object.value(), semaCtx);615  if (!baseObj)616    return std::nullopt;617 618  return getComponentObject(baseObj.value(), semaCtx);619}620 621void generateMemberPlacementIndices(const Object &object,622                                    llvm::SmallVectorImpl<int64_t> &indices,623                                    semantics::SemanticsContext &semaCtx) {624  assert(indices.empty() && "indices vector passed to "625                            "generateMemberPlacementIndices should be empty");626  auto compObj = getComponentObject(object, semaCtx);627 628  while (compObj) {629    int64_t index = getComponentPlacementInParent(compObj->sym());630    assert(631        index >= 0 &&632        "unexpected index value returned from getComponentPlacementInParent");633    indices.push_back(index);634    compObj =635        getComponentObject(getBaseObject(compObj.value(), semaCtx), semaCtx);636  }637 638  indices = llvm::SmallVector<int64_t>{llvm::reverse(indices)};639}640 641void OmpMapParentAndMemberData::addChildIndexAndMapToParent(642    const omp::Object &object, mlir::omp::MapInfoOp &mapOp,643    semantics::SemanticsContext &semaCtx) {644  llvm::SmallVector<int64_t> indices;645  generateMemberPlacementIndices(object, indices, semaCtx);646  memberPlacementIndices.push_back(indices);647  memberMap.push_back(mapOp);648}649 650bool isMemberOrParentAllocatableOrPointer(651    const Object &object, semantics::SemanticsContext &semaCtx) {652  if (semantics::IsAllocatableOrObjectPointer(object.sym()))653    return true;654 655  auto compObj = getBaseObject(object, semaCtx);656  while (compObj) {657    if (semantics::IsAllocatableOrObjectPointer(compObj.value().sym()))658      return true;659    compObj = getBaseObject(compObj.value(), semaCtx);660  }661 662  return false;663}664 665void insertChildMapInfoIntoParent(666    lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,667    lower::StatementContext &stmtCtx,668    std::map<Object, OmpMapParentAndMemberData> &parentMemberIndices,669    llvm::SmallVectorImpl<mlir::Value> &mapOperands,670    llvm::SmallVectorImpl<const semantics::Symbol *> &mapSyms) {671  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();672  for (auto indices : parentMemberIndices) {673    auto *parentIter =674        llvm::find_if(mapSyms, [&indices](const semantics::Symbol *v) {675          return v == indices.first.sym();676        });677    if (parentIter != mapSyms.end()) {678      auto mapOp = llvm::cast<mlir::omp::MapInfoOp>(679          mapOperands[std::distance(mapSyms.begin(), parentIter)]680              .getDefiningOp());681 682      // Once explicit members are attached to a parent map, do not also invoke683      // a declare mapper on it, otherwise the mapper would remap the same684      // components leading to duplicate mappings at runtime.685      if (!indices.second.memberMap.empty() && mapOp.getMapperIdAttr())686        mapOp.setMapperIdAttr(nullptr);687 688      // NOTE: To maintain appropriate SSA ordering, we move the parent map689      // which will now have references to its children after the last690      // of its members to be generated. This is necessary when a user691      // has defined a series of parent and children maps where the parent692      // precedes the children. An alternative, may be to do693      // delayed generation of map info operations from the clauses and694      // organize them first before generation. Or to use the695      // topologicalSort utility which will enforce a stronger SSA696      // dominance ordering at the cost of efficiency/time.697      mapOp->moveAfter(indices.second.memberMap.back());698 699      for (mlir::omp::MapInfoOp memberMap : indices.second.memberMap)700        mapOp.getMembersMutable().append(memberMap.getResult());701 702      mapOp.setMembersIndexAttr(firOpBuilder.create2DI64ArrayAttr(703          indices.second.memberPlacementIndices));704    } else {705      // NOTE: We take the map type of the first child, this may not706      // be the correct thing to do, however, we shall see. For the moment707      // it allows this to work with enter and exit without causing MLIR708      // verification issues. The more appropriate thing may be to take709      // the "main" map type clause from the directive being used.710      mlir::omp::ClauseMapFlags mapType =711          indices.second.memberMap[0].getMapType();712 713      llvm::SmallVector<mlir::Value> members;714      members.reserve(indices.second.memberMap.size());715      for (mlir::omp::MapInfoOp memberMap : indices.second.memberMap)716        members.push_back(memberMap.getResult());717 718      // Create parent to emplace and bind members719      llvm::SmallVector<mlir::Value> bounds;720      std::stringstream asFortran;721      fir::factory::AddrAndBoundsInfo info =722          lower::gatherDataOperandAddrAndBounds<mlir::omp::MapBoundsOp,723                                                mlir::omp::MapBoundsType>(724              converter, firOpBuilder, semaCtx, converter.getFctCtx(),725              *indices.first.sym(), indices.first.ref(),726              converter.getCurrentLocation(), asFortran, bounds,727              treatIndexAsSection);728 729      mlir::omp::MapInfoOp mapOp = utils::openmp::createMapInfoOp(730          firOpBuilder, info.rawInput.getLoc(), info.rawInput,731          /*varPtrPtr=*/mlir::Value(), asFortran.str(), bounds, members,732          firOpBuilder.create2DI64ArrayAttr(733              indices.second.memberPlacementIndices),734          mapType, mlir::omp::VariableCaptureKind::ByRef,735          info.rawInput.getType(),736          /*partialMap=*/true);737 738      mapOperands.push_back(mapOp);739      mapSyms.push_back(indices.first.sym());740    }741  }742}743 744void lastprivateModifierNotSupported(const omp::clause::Lastprivate &lastp,745                                     mlir::Location loc) {746  using Lastprivate = omp::clause::Lastprivate;747  auto &maybeMod =748      std::get<std::optional<Lastprivate::LastprivateModifier>>(lastp.t);749  if (maybeMod) {750    assert(*maybeMod == Lastprivate::LastprivateModifier::Conditional &&751           "Unexpected lastprivate modifier");752    TODO(loc, "lastprivate clause with CONDITIONAL modifier");753  }754}755 756static void convertLoopBounds(lower::AbstractConverter &converter,757                              mlir::Location loc,758                              mlir::omp::LoopRelatedClauseOps &result,759                              std::size_t loopVarTypeSize) {760  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();761  // The types of lower bound, upper bound, and step are converted into the762  // type of the loop variable if necessary.763  mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize);764  for (unsigned it = 0; it < (unsigned)result.loopLowerBounds.size(); it++) {765    result.loopLowerBounds[it] = firOpBuilder.createConvert(766        loc, loopVarType, result.loopLowerBounds[it]);767    result.loopUpperBounds[it] = firOpBuilder.createConvert(768        loc, loopVarType, result.loopUpperBounds[it]);769    result.loopSteps[it] =770        firOpBuilder.createConvert(loc, loopVarType, result.loopSteps[it]);771  }772}773 774// Helper function that finds the sizes clause in a inner OMPD_tile directive775// and passes the sizes clause to the callback function if found.776static void processTileSizesFromOpenMPConstruct(777    const parser::OpenMPConstruct *ompCons,778    std::function<void(const parser::OmpClause::Sizes *)> processFun) {779  if (!ompCons)780    return;781  if (auto *ompLoop{std::get_if<parser::OpenMPLoopConstruct>(&ompCons->u)}) {782    if (auto *innerConstruct = ompLoop->GetNestedConstruct()) {783      const parser::OmpDirectiveSpecification &innerBeginSpec =784          innerConstruct->BeginDir();785      if (innerBeginSpec.DirId() == llvm::omp::Directive::OMPD_tile) {786        // Get the size values from parse tree and convert to a vector.787        for (const auto &clause : innerBeginSpec.Clauses().v) {788          if (const auto tclause{789                  std::get_if<parser::OmpClause::Sizes>(&clause.u)}) {790            processFun(tclause);791            break;792          }793        }794      }795    }796  }797}798 799pft::Evaluation *getNestedDoConstruct(pft::Evaluation &eval) {800  for (pft::Evaluation &nested : eval.getNestedEvaluations()) {801    // In an OpenMPConstruct there can be compiler directives:802    // 1 <<OpenMPConstruct>>803    //     2 CompilerDirective: !unroll804    //     <<DoConstruct>> -> 8805    if (nested.getIf<parser::CompilerDirective>())806      continue;807    // Within a DoConstruct, there can be compiler directives, plus808    // there is a DoStmt before the body:809    // <<DoConstruct>> -> 8810    //     3 NonLabelDoStmt -> 7: do i = 1, n811    //     <<DoConstruct>> -> 7812    if (nested.getIf<parser::NonLabelDoStmt>())813      continue;814    assert(nested.getIf<parser::DoConstruct>() &&815           "Unexpected construct in the nested evaluations");816    return &nested;817  }818  llvm_unreachable("Expected do loop to be in the nested evaluations");819}820 821/// Populates the sizes vector with values if the given OpenMPConstruct822/// contains a loop construct with an inner tiling construct.823void collectTileSizesFromOpenMPConstruct(824    const parser::OpenMPConstruct *ompCons,825    llvm::SmallVectorImpl<int64_t> &tileSizes,826    Fortran::semantics::SemanticsContext &semaCtx) {827  processTileSizesFromOpenMPConstruct(828      ompCons, [&](const parser::OmpClause::Sizes *tclause) {829        for (auto &tval : tclause->v)830          if (const auto v{EvaluateInt64(semaCtx, tval)})831            tileSizes.push_back(*v);832      });833}834 835int64_t collectLoopRelatedInfo(836    lower::AbstractConverter &converter, mlir::Location currentLocation,837    lower::pft::Evaluation &eval, const omp::List<omp::Clause> &clauses,838    mlir::omp::LoopRelatedClauseOps &result,839    llvm::SmallVectorImpl<const semantics::Symbol *> &iv) {840  int64_t numCollapse = 1;841 842  // Collect the loops to collapse.843  lower::pft::Evaluation *doConstructEval = getNestedDoConstruct(eval);844  if (doConstructEval->getIf<parser::DoConstruct>()->IsDoConcurrent()) {845    TODO(currentLocation, "Do Concurrent in Worksharing loop construct");846  }847 848  std::int64_t collapseValue = 1l;849  if (auto *clause =850          ClauseFinder::findUniqueClause<omp::clause::Collapse>(clauses)) {851    collapseValue = evaluate::ToInt64(clause->v).value();852    numCollapse = collapseValue;853  }854 855  collectLoopRelatedInfo(converter, currentLocation, eval, numCollapse, result,856                         iv);857  return numCollapse;858}859 860void collectLoopRelatedInfo(861    lower::AbstractConverter &converter, mlir::Location currentLocation,862    lower::pft::Evaluation &eval, int64_t numCollapse,863    mlir::omp::LoopRelatedClauseOps &result,864    llvm::SmallVectorImpl<const semantics::Symbol *> &iv) {865 866  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();867 868  // Collect the loops to collapse.869  lower::pft::Evaluation *doConstructEval = getNestedDoConstruct(eval);870  if (doConstructEval->getIf<parser::DoConstruct>()->IsDoConcurrent()) {871    TODO(currentLocation, "Do Concurrent in Worksharing loop construct");872  }873 874  // Collect sizes from tile directive if present.875  std::int64_t sizesLengthValue = 0l;876  if (auto *ompCons{eval.getIf<parser::OpenMPConstruct>()}) {877    processTileSizesFromOpenMPConstruct(878        ompCons, [&](const parser::OmpClause::Sizes *tclause) {879          sizesLengthValue = tclause->v.size();880        });881  }882 883  std::int64_t collapseValue = std::max(numCollapse, sizesLengthValue);884  std::size_t loopVarTypeSize = 0;885  do {886    lower::pft::Evaluation *doLoop =887        &doConstructEval->getFirstNestedEvaluation();888    auto *doStmt = doLoop->getIf<parser::NonLabelDoStmt>();889    assert(doStmt && "Expected do loop to be in the nested evaluation");890    const auto &loopControl =891        std::get<std::optional<parser::LoopControl>>(doStmt->t);892    const parser::LoopControl::Bounds *bounds =893        std::get_if<parser::LoopControl::Bounds>(&loopControl->u);894    assert(bounds && "Expected bounds for worksharing do loop");895    lower::StatementContext stmtCtx;896    result.loopLowerBounds.push_back(fir::getBase(897        converter.genExprValue(*semantics::GetExpr(bounds->lower), stmtCtx)));898    result.loopUpperBounds.push_back(fir::getBase(899        converter.genExprValue(*semantics::GetExpr(bounds->upper), stmtCtx)));900    if (bounds->step) {901      result.loopSteps.push_back(fir::getBase(902          converter.genExprValue(*semantics::GetExpr(bounds->step), stmtCtx)));903    } else { // If `step` is not present, assume it as `1`.904      result.loopSteps.push_back(firOpBuilder.createIntegerConstant(905          currentLocation, firOpBuilder.getIntegerType(32), 1));906    }907    iv.push_back(bounds->name.thing.symbol);908    loopVarTypeSize = std::max(loopVarTypeSize,909                               bounds->name.thing.symbol->GetUltimate().size());910    if (--collapseValue)911      doConstructEval = getNestedDoConstruct(*doConstructEval);912  } while (collapseValue > 0);913 914  convertLoopBounds(converter, currentLocation, result, loopVarTypeSize);915}916 917} // namespace omp918} // namespace lower919} // namespace Fortran920