| //===-- Utils..cpp ----------------------------------------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "Utils.h" |
| |
| #include "Clauses.h" |
| #include <flang/Lower/AbstractConverter.h> |
| #include <flang/Lower/ConvertType.h> |
| #include <flang/Lower/PFTBuilder.h> |
| #include <flang/Optimizer/Builder/FIRBuilder.h> |
| #include <flang/Parser/parse-tree.h> |
| #include <flang/Parser/tools.h> |
| #include <flang/Semantics/tools.h> |
| #include <llvm/Support/CommandLine.h> |
| |
| #include <algorithm> |
| #include <numeric> |
| |
| llvm::cl::opt<bool> treatIndexAsSection( |
| "openmp-treat-index-as-section", |
| llvm::cl::desc("In the OpenMP data clauses treat `a(N)` as `a(N:N)`."), |
| llvm::cl::init(true)); |
| |
| llvm::cl::opt<bool> enableDelayedPrivatization( |
| "openmp-enable-delayed-privatization", |
| llvm::cl::desc( |
| "Emit `[first]private` variables as clauses on the MLIR ops."), |
| llvm::cl::init(false)); |
| |
| llvm::cl::opt<bool> enableDelayedPrivatizationStaging( |
| "openmp-enable-delayed-privatization-staging", |
| llvm::cl::desc("For partially supported constructs, emit `[first]private` " |
| "variables as clauses on the MLIR ops."), |
| llvm::cl::init(false)); |
| |
| namespace Fortran { |
| namespace lower { |
| namespace omp { |
| |
| int64_t getCollapseValue(const List<Clause> &clauses) { |
| auto iter = llvm::find_if(clauses, [](const Clause &clause) { |
| return clause.id == llvm::omp::Clause::OMPC_collapse; |
| }); |
| if (iter != clauses.end()) { |
| const auto &collapse = std::get<clause::Collapse>(iter->u); |
| return evaluate::ToInt64(collapse.v).value(); |
| } |
| return 1; |
| } |
| |
| void genObjectList(const ObjectList &objects, |
| lower::AbstractConverter &converter, |
| llvm::SmallVectorImpl<mlir::Value> &operands) { |
| for (const Object &object : objects) { |
| const semantics::Symbol *sym = object.sym(); |
| assert(sym && "Expected Symbol"); |
| if (mlir::Value variable = converter.getSymbolAddress(*sym)) { |
| operands.push_back(variable); |
| } else if (const auto *details = |
| sym->detailsIf<semantics::HostAssocDetails>()) { |
| operands.push_back(converter.getSymbolAddress(details->symbol())); |
| converter.copySymbolBinding(details->symbol(), *sym); |
| } |
| } |
| } |
| |
| mlir::Type getLoopVarType(lower::AbstractConverter &converter, |
| std::size_t loopVarTypeSize) { |
| // OpenMP runtime requires 32-bit or 64-bit loop variables. |
| loopVarTypeSize = loopVarTypeSize * 8; |
| if (loopVarTypeSize < 32) { |
| loopVarTypeSize = 32; |
| } else if (loopVarTypeSize > 64) { |
| loopVarTypeSize = 64; |
| mlir::emitWarning(converter.getCurrentLocation(), |
| "OpenMP loop iteration variable cannot have more than 64 " |
| "bits size and will be narrowed into 64 bits."); |
| } |
| assert((loopVarTypeSize == 32 || loopVarTypeSize == 64) && |
| "OpenMP loop iteration variable size must be transformed into 32-bit " |
| "or 64-bit"); |
| return converter.getFirOpBuilder().getIntegerType(loopVarTypeSize); |
| } |
| |
| semantics::Symbol * |
| getIterationVariableSymbol(const lower::pft::Evaluation &eval) { |
| return eval.visit(common::visitors{ |
| [&](const parser::DoConstruct &doLoop) { |
| if (const auto &maybeCtrl = doLoop.GetLoopControl()) { |
| using LoopControl = parser::LoopControl; |
| if (auto *bounds = std::get_if<LoopControl::Bounds>(&maybeCtrl->u)) { |
| static_assert(std::is_same_v<decltype(bounds->name), |
| parser::Scalar<parser::Name>>); |
| return bounds->name.thing.symbol; |
| } |
| } |
| return static_cast<semantics::Symbol *>(nullptr); |
| }, |
| [](auto &&) { return static_cast<semantics::Symbol *>(nullptr); }, |
| }); |
| } |
| |
| void gatherFuncAndVarSyms( |
| const ObjectList &objects, mlir::omp::DeclareTargetCaptureClause clause, |
| llvm::SmallVectorImpl<DeclareTargetCapturePair> &symbolAndClause) { |
| for (const Object &object : objects) |
| symbolAndClause.emplace_back(clause, *object.sym()); |
| } |
| |
| mlir::omp::MapInfoOp |
| createMapInfoOp(fir::FirOpBuilder &builder, mlir::Location loc, |
| mlir::Value baseAddr, mlir::Value varPtrPtr, std::string name, |
| llvm::ArrayRef<mlir::Value> bounds, |
| llvm::ArrayRef<mlir::Value> members, |
| mlir::DenseIntElementsAttr membersIndex, uint64_t mapType, |
| mlir::omp::VariableCaptureKind mapCaptureType, mlir::Type retTy, |
| bool partialMap) { |
| if (auto boxTy = llvm::dyn_cast<fir::BaseBoxType>(baseAddr.getType())) { |
| baseAddr = builder.create<fir::BoxAddrOp>(loc, baseAddr); |
| retTy = baseAddr.getType(); |
| } |
| |
| mlir::TypeAttr varType = mlir::TypeAttr::get( |
| llvm::cast<mlir::omp::PointerLikeType>(retTy).getElementType()); |
| |
| mlir::omp::MapInfoOp op = builder.create<mlir::omp::MapInfoOp>( |
| loc, retTy, baseAddr, varType, varPtrPtr, members, membersIndex, bounds, |
| builder.getIntegerAttr(builder.getIntegerType(64, false), mapType), |
| builder.getAttr<mlir::omp::VariableCaptureKindAttr>(mapCaptureType), |
| builder.getStringAttr(name), builder.getBoolAttr(partialMap)); |
| |
| return op; |
| } |
| |
| static int |
| getComponentPlacementInParent(const semantics::Symbol *componentSym) { |
| const auto *derived = componentSym->owner() |
| .derivedTypeSpec() |
| ->typeSymbol() |
| .detailsIf<semantics::DerivedTypeDetails>(); |
| assert(derived && |
| "expected derived type details when processing component symbol"); |
| for (auto [placement, name] : llvm::enumerate(derived->componentNames())) |
| if (name == componentSym->name()) |
| return placement; |
| return -1; |
| } |
| |
| static std::optional<Object> |
| getComponentObject(std::optional<Object> object, |
| semantics::SemanticsContext &semaCtx) { |
| if (!object) |
| return std::nullopt; |
| |
| auto ref = evaluate::ExtractDataRef(*object.value().ref()); |
| if (!ref) |
| return std::nullopt; |
| |
| if (std::holds_alternative<evaluate::Component>(ref->u)) |
| return object; |
| |
| auto baseObj = getBaseObject(object.value(), semaCtx); |
| if (!baseObj) |
| return std::nullopt; |
| |
| return getComponentObject(baseObj.value(), semaCtx); |
| } |
| |
| static void |
| generateMemberPlacementIndices(const Object &object, |
| llvm::SmallVectorImpl<int> &indices, |
| semantics::SemanticsContext &semaCtx) { |
| auto compObj = getComponentObject(object, semaCtx); |
| while (compObj) { |
| indices.push_back(getComponentPlacementInParent(compObj->sym())); |
| compObj = |
| getComponentObject(getBaseObject(compObj.value(), semaCtx), semaCtx); |
| } |
| |
| indices = llvm::SmallVector<int>{llvm::reverse(indices)}; |
| } |
| |
| void addChildIndexAndMapToParent( |
| const omp::Object &object, |
| std::map<const semantics::Symbol *, |
| llvm::SmallVector<OmpMapMemberIndicesData>> &parentMemberIndices, |
| mlir::omp::MapInfoOp &mapOp, semantics::SemanticsContext &semaCtx) { |
| std::optional<evaluate::DataRef> dataRef = ExtractDataRef(object.ref()); |
| assert(dataRef.has_value() && |
| "DataRef could not be extracted during mapping of derived type " |
| "cannot proceed"); |
| const semantics::Symbol *parentSym = &dataRef->GetFirstSymbol(); |
| assert(parentSym && "Could not find parent symbol during lower of " |
| "a component member in OpenMP map clause"); |
| llvm::SmallVector<int> indices; |
| generateMemberPlacementIndices(object, indices, semaCtx); |
| parentMemberIndices[parentSym].push_back({indices, mapOp}); |
| } |
| |
| static void calculateShapeAndFillIndices( |
| llvm::SmallVectorImpl<int64_t> &shape, |
| llvm::SmallVectorImpl<OmpMapMemberIndicesData> &memberPlacementData) { |
| shape.push_back(memberPlacementData.size()); |
| size_t largestIndicesSize = |
| std::max_element(memberPlacementData.begin(), memberPlacementData.end(), |
| [](auto a, auto b) { |
| return a.memberPlacementIndices.size() < |
| b.memberPlacementIndices.size(); |
| }) |
| ->memberPlacementIndices.size(); |
| shape.push_back(largestIndicesSize); |
| |
| // DenseElementsAttr expects a rectangular shape for the data, so all |
| // index lists have to be of the same length, this emplaces -1 as filler. |
| for (auto &v : memberPlacementData) { |
| if (v.memberPlacementIndices.size() < largestIndicesSize) { |
| auto *prevEnd = v.memberPlacementIndices.end(); |
| v.memberPlacementIndices.resize(largestIndicesSize); |
| std::fill(prevEnd, v.memberPlacementIndices.end(), -1); |
| } |
| } |
| } |
| |
| static mlir::DenseIntElementsAttr createDenseElementsAttrFromIndices( |
| llvm::SmallVectorImpl<OmpMapMemberIndicesData> &memberPlacementData, |
| fir::FirOpBuilder &builder) { |
| llvm::SmallVector<int64_t> shape; |
| calculateShapeAndFillIndices(shape, memberPlacementData); |
| |
| llvm::SmallVector<int> indicesFlattened = |
| std::accumulate(memberPlacementData.begin(), memberPlacementData.end(), |
| llvm::SmallVector<int>(), |
| [](llvm::SmallVector<int> &x, OmpMapMemberIndicesData y) { |
| x.insert(x.end(), y.memberPlacementIndices.begin(), |
| y.memberPlacementIndices.end()); |
| return x; |
| }); |
| |
| return mlir::DenseIntElementsAttr::get( |
| mlir::VectorType::get(shape, |
| mlir::IntegerType::get(builder.getContext(), 32)), |
| indicesFlattened); |
| } |
| |
| void insertChildMapInfoIntoParent( |
| lower::AbstractConverter &converter, |
| std::map<const semantics::Symbol *, |
| llvm::SmallVector<OmpMapMemberIndicesData>> &parentMemberIndices, |
| llvm::SmallVectorImpl<mlir::Value> &mapOperands, |
| llvm::SmallVectorImpl<const semantics::Symbol *> &mapSyms, |
| llvm::SmallVectorImpl<mlir::Type> *mapSymTypes, |
| llvm::SmallVectorImpl<mlir::Location> *mapSymLocs) { |
| for (auto indices : parentMemberIndices) { |
| bool parentExists = false; |
| size_t parentIdx; |
| for (parentIdx = 0; parentIdx < mapSyms.size(); ++parentIdx) { |
| if (mapSyms[parentIdx] == indices.first) { |
| parentExists = true; |
| break; |
| } |
| } |
| |
| if (parentExists) { |
| auto mapOp = llvm::cast<mlir::omp::MapInfoOp>( |
| mapOperands[parentIdx].getDefiningOp()); |
| |
| // NOTE: To maintain appropriate SSA ordering, we move the parent map |
| // which will now have references to its children after the last |
| // of its members to be generated. This is necessary when a user |
| // has defined a series of parent and children maps where the parent |
| // precedes the children. An alternative, may be to do |
| // delayed generation of map info operations from the clauses and |
| // organize them first before generation. |
| mapOp->moveAfter(indices.second.back().memberMap); |
| |
| for (auto memberIndicesData : indices.second) |
| mapOp.getMembersMutable().append( |
| memberIndicesData.memberMap.getResult()); |
| |
| mapOp.setMembersIndexAttr(createDenseElementsAttrFromIndices( |
| indices.second, converter.getFirOpBuilder())); |
| } else { |
| // NOTE: We take the map type of the first child, this may not |
| // be the correct thing to do, however, we shall see. For the moment |
| // it allows this to work with enter and exit without causing MLIR |
| // verification issues. The more appropriate thing may be to take |
| // the "main" map type clause from the directive being used. |
| uint64_t mapType = indices.second[0].memberMap.getMapType().value_or(0); |
| |
| // create parent to emplace and bind members |
| mlir::Value origSymbol = converter.getSymbolAddress(*indices.first); |
| |
| llvm::SmallVector<mlir::Value> members; |
| for (OmpMapMemberIndicesData memberIndicesData : indices.second) |
| members.push_back((mlir::Value)memberIndicesData.memberMap); |
| |
| mlir::Value mapOp = createMapInfoOp( |
| converter.getFirOpBuilder(), origSymbol.getLoc(), origSymbol, |
| /*varPtrPtr=*/mlir::Value(), indices.first->name().ToString(), |
| /*bounds=*/{}, members, |
| createDenseElementsAttrFromIndices(indices.second, |
| converter.getFirOpBuilder()), |
| mapType, mlir::omp::VariableCaptureKind::ByRef, origSymbol.getType(), |
| /*partialMap=*/true); |
| |
| mapOperands.push_back(mapOp); |
| mapSyms.push_back(indices.first); |
| |
| if (mapSymTypes) |
| mapSymTypes->push_back(mapOp.getType()); |
| if (mapSymLocs) |
| mapSymLocs->push_back(mapOp.getLoc()); |
| } |
| } |
| } |
| |
| semantics::Symbol *getOmpObjectSymbol(const parser::OmpObject &ompObject) { |
| semantics::Symbol *sym = nullptr; |
| Fortran::common::visit( |
| common::visitors{ |
| [&](const parser::Designator &designator) { |
| if (auto *arrayEle = |
| parser::Unwrap<parser::ArrayElement>(designator)) { |
| // Use getLastName to retrieve the arrays symbol, this will |
| // provide the farthest right symbol (the last) in a designator, |
| // i.e. providing something like the following: |
| // "dtype1%dtype2%array[2:10]", will result in "array" |
| sym = GetLastName(arrayEle->base).symbol; |
| } else if (auto *structComp = |
| parser::Unwrap<parser::StructureComponent>( |
| designator)) { |
| sym = structComp->component.symbol; |
| } else if (const parser::Name *name = |
| semantics::getDesignatorNameIfDataRef(designator)) { |
| sym = name->symbol; |
| } |
| }, |
| [&](const parser::Name &name) { sym = name.symbol; }}, |
| ompObject.u); |
| return sym; |
| } |
| |
| } // namespace omp |
| } // namespace lower |
| } // namespace Fortran |