//===- OMPDescriptorMapInfoGen.cpp //---------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// /// \file /// An OpenMP dialect related pass for FIR/HLFIR which expands MapInfoOp's /// containing descriptor related types (fir::BoxType's) into multiple /// MapInfoOp's containing the parent descriptor and pointer member components /// for individual mapping, treating the descriptor type as a record type for /// later lowering in the OpenMP dialect. //===----------------------------------------------------------------------===// #include "flang/Optimizer/Builder/FIRBuilder.h" #include "flang/Optimizer/Dialect/FIRType.h" #include "flang/Optimizer/Dialect/Support/KindMapping.h" #include "flang/Optimizer/Transforms/Passes.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/OpenMP/OpenMPDialect.h" #include "mlir/IR/BuiltinDialect.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/Operation.h" #include "mlir/IR/SymbolTable.h" #include "mlir/Pass/Pass.h" #include "mlir/Support/LLVM.h" #include "llvm/ADT/SmallPtrSet.h" #include namespace fir { #define GEN_PASS_DEF_OMPDESCRIPTORMAPINFOGENPASS #include "flang/Optimizer/Transforms/Passes.h.inc" } // namespace fir namespace { class OMPDescriptorMapInfoGenPass : public fir::impl::OMPDescriptorMapInfoGenPassBase< OMPDescriptorMapInfoGenPass> { void genDescriptorMemberMaps(mlir::omp::MapInfoOp op, fir::FirOpBuilder &builder, mlir::Operation *target) { mlir::Location loc = builder.getUnknownLoc(); mlir::Value descriptor = op.getVarPtr(); // If we enter this function, but the mapped type itself is not the // descriptor, then it's likely the address of the descriptor so we // must retrieve the descriptor SSA. if (!fir::isTypeWithDescriptor(op.getVarType())) { if (auto addrOp = mlir::dyn_cast_if_present( op.getVarPtr().getDefiningOp())) { descriptor = addrOp.getVal(); } } // The fir::BoxOffsetOp only works with !fir.ref> types, as // allowing it to access non-reference box operations can cause some // problematic SSA IR. However, in the case of assumed shape's the type // is not a !fir.ref, in these cases to retrieve the appropriate // !fir.ref> to access the data we need to map we must // perform an alloca and then store to it and retrieve the data from the new // alloca. if (mlir::isa(descriptor.getType())) { mlir::OpBuilder::InsertPoint insPt = builder.saveInsertionPoint(); builder.setInsertionPointToStart(builder.getAllocaBlock()); auto alloca = builder.create(loc, descriptor.getType()); builder.restoreInsertionPoint(insPt); builder.create(loc, descriptor, alloca); descriptor = alloca; } mlir::Value baseAddrAddr = builder.create( loc, descriptor, fir::BoxFieldAttr::base_addr); // Member of the descriptor pointing at the allocated data mlir::Value baseAddr = builder.create( loc, baseAddrAddr.getType(), descriptor, llvm::cast( fir::unwrapRefType(baseAddrAddr.getType())) .getElementType(), baseAddrAddr, mlir::SmallVector{}, op.getBounds(), builder.getIntegerAttr(builder.getIntegerType(64, false), op.getMapType().value()), builder.getAttr( mlir::omp::VariableCaptureKind::ByRef), builder.getStringAttr("") /*name*/); // TODO: map the addendum segment of the descriptor, similarly to the // above base address/data pointer member. if (auto mapClauseOwner = llvm::dyn_cast(target)) { llvm::SmallVector newMapOps; mlir::OperandRange mapOperandsArr = mapClauseOwner.getMapOperands(); for (size_t i = 0; i < mapOperandsArr.size(); ++i) { if (mapOperandsArr[i] == op) { // Push new implicit maps generated for the descriptor. newMapOps.push_back(baseAddr); // for TargetOp's which have IsolatedFromAbove we must align the // new additional map operand with an appropriate BlockArgument, // as the printing and later processing currently requires a 1:1 // mapping of BlockArgs to MapInfoOp's at the same placement in // each array (BlockArgs and MapOperands). if (auto targetOp = llvm::dyn_cast(target)) targetOp.getRegion().insertArgument(i, baseAddr.getType(), loc); } newMapOps.push_back(mapOperandsArr[i]); } mapClauseOwner.getMapOperandsMutable().assign(newMapOps); } mlir::Value newDescParentMapOp = builder.create( op->getLoc(), op.getResult().getType(), descriptor, fir::unwrapRefType(descriptor.getType()), mlir::Value{}, mlir::SmallVector{baseAddr}, mlir::SmallVector{}, builder.getIntegerAttr(builder.getIntegerType(64, false), op.getMapType().value()), op.getMapCaptureTypeAttr(), op.getNameAttr()); op.replaceAllUsesWith(newDescParentMapOp); op->erase(); } // This pass executes on mlir::ModuleOp's finding omp::MapInfoOp's containing // descriptor based types (allocatables, pointers, assumed shape etc.) and // expanding them into multiple omp::MapInfoOp's for each pointer member // contained within the descriptor. void runOnOperation() override { mlir::func::FuncOp func = getOperation(); mlir::ModuleOp module = func->getParentOfType(); fir::KindMapping kindMap = fir::getKindMapping(module); fir::FirOpBuilder builder{module, std::move(kindMap)}; func->walk([&](mlir::omp::MapInfoOp op) { if (fir::isTypeWithDescriptor(op.getVarType()) || mlir::isa_and_present( op.getVarPtr().getDefiningOp())) { builder.setInsertionPoint(op); // TODO: Currently only supports a single user for the MapInfoOp, this // is fine for the moment as the Fortran Frontend will generate a // new MapInfoOp per Target operation for the moment. However, when/if // we optimise/cleanup the IR, it likely isn't too difficult to // extend this function, it would require some modification to create a // single new MapInfoOp per new MapInfoOp generated and share it across // all users appropriately, making sure to only add a single member link // per new generation for the original originating descriptor MapInfoOp. assert(llvm::hasSingleElement(op->getUsers()) && "OMPDescriptorMapInfoGen currently only supports single users " "of a MapInfoOp"); genDescriptorMemberMaps(op, builder, *op->getUsers().begin()); } }); } }; } // namespace namespace fir { std::unique_ptr createOMPDescriptorMapInfoGenPass() { return std::make_unique(); } } // namespace fir