//===-- 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 #include #include #include #include #include #include #include #include #include llvm::cl::opt 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 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 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 &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(iter->u); return evaluate::ToInt64(collapse.v).value(); } return 1; } void genObjectList(const ObjectList &objects, lower::AbstractConverter &converter, llvm::SmallVectorImpl &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()) { 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(&maybeCtrl->u)) { static_assert(std::is_same_vname), parser::Scalar>); return bounds->name.thing.symbol; } } return static_cast(nullptr); }, [](auto &&) { return static_cast(nullptr); }, }); } void gatherFuncAndVarSyms( const ObjectList &objects, mlir::omp::DeclareTargetCaptureClause clause, llvm::SmallVectorImpl &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 bounds, llvm::ArrayRef members, mlir::DenseIntElementsAttr membersIndex, uint64_t mapType, mlir::omp::VariableCaptureKind mapCaptureType, mlir::Type retTy, bool partialMap) { if (auto boxTy = llvm::dyn_cast(baseAddr.getType())) { baseAddr = builder.create(loc, baseAddr); retTy = baseAddr.getType(); } mlir::TypeAttr varType = mlir::TypeAttr::get( llvm::cast(retTy).getElementType()); mlir::omp::MapInfoOp op = builder.create( loc, retTy, baseAddr, varType, varPtrPtr, members, membersIndex, bounds, builder.getIntegerAttr(builder.getIntegerType(64, false), mapType), builder.getAttr(mapCaptureType), builder.getStringAttr(name), builder.getBoolAttr(partialMap)); return op; } static int getComponentPlacementInParent(const semantics::Symbol *componentSym) { const auto *derived = componentSym->owner() .derivedTypeSpec() ->typeSymbol() .detailsIf(); 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 getComponentObject(std::optional 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(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 &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{llvm::reverse(indices)}; } void addChildIndexAndMapToParent( const omp::Object &object, std::map> &parentMemberIndices, mlir::omp::MapInfoOp &mapOp, semantics::SemanticsContext &semaCtx) { std::optional 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 indices; generateMemberPlacementIndices(object, indices, semaCtx); parentMemberIndices[parentSym].push_back({indices, mapOp}); } static void calculateShapeAndFillIndices( llvm::SmallVectorImpl &shape, llvm::SmallVectorImpl &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 &memberPlacementData, fir::FirOpBuilder &builder) { llvm::SmallVector shape; calculateShapeAndFillIndices(shape, memberPlacementData); llvm::SmallVector indicesFlattened = std::accumulate(memberPlacementData.begin(), memberPlacementData.end(), llvm::SmallVector(), [](llvm::SmallVector &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> &parentMemberIndices, llvm::SmallVectorImpl &mapOperands, llvm::SmallVectorImpl &mapSyms, llvm::SmallVectorImpl *mapSymTypes, llvm::SmallVectorImpl *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( 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 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(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( 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