//===-- lib/Utisl/OpenMP.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 // //===----------------------------------------------------------------------===// #include "flang/Utils/OpenMP.h" #include "flang/Lower/ConvertExprToHLFIR.h" #include "flang/Optimizer/Builder/DirectivesCommon.h" #include "flang/Optimizer/Builder/FIRBuilder.h" #include "flang/Optimizer/Dialect/FIROps.h" #include "flang/Optimizer/Dialect/FIRType.h" #include "mlir/Dialect/OpenMP/OpenMPDialect.h" #include "mlir/Transforms/RegionUtils.h" namespace Fortran::utils::openmp { mlir::omp::MapInfoOp createMapInfoOp(mlir::OpBuilder &builder, mlir::Location loc, mlir::Value baseAddr, mlir::Value varPtrPtr, llvm::StringRef name, llvm::ArrayRef bounds, llvm::ArrayRef members, mlir::ArrayAttr membersIndex, mlir::omp::ClauseMapFlags mapType, mlir::omp::VariableCaptureKind mapCaptureType, mlir::Type retTy, bool partialMap, mlir::FlatSymbolRefAttr mapperId) { if (auto boxTy = llvm::dyn_cast(baseAddr.getType())) { baseAddr = fir::BoxAddrOp::create(builder, loc, baseAddr); retTy = baseAddr.getType(); } mlir::TypeAttr varType = mlir::TypeAttr::get( llvm::cast(retTy).getElementType()); // For types with unknown extents such as <2x?xi32> we discard the incomplete // type info and only retain the base type. The correct dimensions are later // recovered through the bounds info. if (auto seqType = llvm::dyn_cast(varType.getValue())) if (seqType.hasDynamicExtents()) varType = mlir::TypeAttr::get(seqType.getEleTy()); mlir::omp::MapInfoOp op = mlir::omp::MapInfoOp::create(builder, loc, retTy, baseAddr, varType, builder.getAttr(mapType), builder.getAttr(mapCaptureType), varPtrPtr, members, membersIndex, bounds, mapperId, builder.getStringAttr(name), builder.getBoolAttr(partialMap)); return op; } mlir::Value mapTemporaryValue(fir::FirOpBuilder &firOpBuilder, mlir::omp::TargetOp targetOp, mlir::Value val, llvm::StringRef name) { mlir::OpBuilder::InsertionGuard guard(firOpBuilder); mlir::Operation *valOp = val.getDefiningOp(); if (valOp) firOpBuilder.setInsertionPointAfter(valOp); else // This means val is a block argument firOpBuilder.setInsertionPoint(targetOp); auto copyVal = firOpBuilder.createTemporary(val.getLoc(), val.getType()); firOpBuilder.createStoreWithConvert(copyVal.getLoc(), val, copyVal); fir::factory::AddrAndBoundsInfo info = fir::factory::getDataOperandBaseAddr( firOpBuilder, val, /*isOptional=*/false, val.getLoc()); llvm::SmallVector bounds = fir::factory::genImplicitBoundsOps(firOpBuilder, info, hlfir::translateToExtendedValue( val.getLoc(), firOpBuilder, hlfir::Entity{val}) .first, /*dataExvIsAssumedSize=*/false, val.getLoc()); firOpBuilder.setInsertionPoint(targetOp); mlir::omp::ClauseMapFlags mapFlag = mlir::omp::ClauseMapFlags::implicit; mlir::omp::VariableCaptureKind captureKind = mlir::omp::VariableCaptureKind::ByRef; mlir::Type eleType = copyVal.getType(); if (auto refType = mlir::dyn_cast(copyVal.getType())) { eleType = refType.getElementType(); } if (fir::isa_trivial(eleType) || fir::isa_char(eleType)) { captureKind = mlir::omp::VariableCaptureKind::ByCopy; } else if (!fir::isa_builtin_cptr_type(eleType)) { mapFlag |= mlir::omp::ClauseMapFlags::to; } mlir::Value mapOp = createMapInfoOp(firOpBuilder, copyVal.getLoc(), copyVal, /*varPtrPtr=*/mlir::Value{}, name.str(), bounds, /*members=*/llvm::SmallVector{}, /*membersIndex=*/mlir::ArrayAttr{}, mapFlag, captureKind, copyVal.getType()); auto argIface = llvm::cast(*targetOp); mlir::Region ®ion = targetOp.getRegion(); // Get the index of the first non-map argument before modifying mapVars, // then append an element to mapVars and an associated entry block // argument at that index. unsigned insertIndex = argIface.getMapBlockArgsStart() + argIface.numMapBlockArgs(); targetOp.getMapVarsMutable().append(mapOp); mlir::Value clonedValArg = region.insertArgument(insertIndex, copyVal.getType(), copyVal.getLoc()); mlir::Block *entryBlock = ®ion.getBlocks().front(); firOpBuilder.setInsertionPointToStart(entryBlock); auto loadOp = fir::LoadOp::create(firOpBuilder, clonedValArg.getLoc(), clonedValArg); return loadOp.getResult(); } void cloneOrMapRegionOutsiders( fir::FirOpBuilder &firOpBuilder, mlir::omp::TargetOp targetOp) { mlir::Region ®ion = targetOp.getRegion(); mlir::Block *entryBlock = ®ion.getBlocks().front(); llvm::SetVector valuesDefinedAbove; mlir::getUsedValuesDefinedAbove(region, valuesDefinedAbove); while (!valuesDefinedAbove.empty()) { for (mlir::Value val : valuesDefinedAbove) { mlir::Operation *valOp = val.getDefiningOp(); // NOTE: We skip BoxDimsOp's as the lesser of two evils is to map the // indices separately, as the alternative is to eventually map the Box, // which comes with a fairly large overhead comparatively. We could be // more robust about this and check using a BackwardsSlice to see if we // run the risk of mapping a box. if (valOp && mlir::isMemoryEffectFree(valOp) && !mlir::isa(valOp)) { mlir::Operation *clonedOp = valOp->clone(); entryBlock->push_front(clonedOp); auto replace = [entryBlock](mlir::OpOperand &use) { return use.getOwner()->getBlock() == entryBlock; }; valOp->getResults().replaceUsesWithIf(clonedOp->getResults(), replace); valOp->replaceUsesWithIf(clonedOp, replace); } else { mlir::Value mappedTemp = mapTemporaryValue(firOpBuilder, targetOp, val, /*name=*/{}); val.replaceUsesWithIf(mappedTemp, [entryBlock](mlir::OpOperand &use) { return use.getOwner()->getBlock() == entryBlock; }); } } valuesDefinedAbove.clear(); mlir::getUsedValuesDefinedAbove(region, valuesDefinedAbove); } } } // namespace Fortran::utils::openmp