Files
llvm-project/flang/lib/Optimizer/CodeGen/CodeGenOpenMP.cpp
agozillon a16668a8d7 [Flang][OpenMP][MLIR] Align declare mapper pass handling with other map and global operations (#176852)
This PR makes a couple of minor tweaks to the lowering for
declare_mapper operations:

1) Add declare_mapper operations to the list of global operations to
have optimisation passes
executed on them. Primarily just to make sure we keep it inline with
other global operations
that contain regions. Prevents oddities where we embed FIR/HLFIR into
the mapper that needs
lowered before being converted to LLVM-IR. One example that springs to
mind is if we ever
decide to remove the single block condition on the operation to allow
conditional checks
   for mapped data.
2) Add a CodeGenOpenMP.cpp conversion for DeclareMapperOp to make sure
we convert the return
type correctly from a BoxType to a struct type rather than an opaque
pointer when lowering.
Currently, I've left out the block argument types from being converted
as they're wrapped
   in a fir.ref and would be opauqe pointers in either case.

So some minor additions to keep declare_mapper a little more inline with
the rest of the OpenMP operations.
2026-01-23 22:36:39 +01:00

287 lines
13 KiB
C++

//===-- CodeGenOpenMP.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
//
//===----------------------------------------------------------------------===//
//
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
//
//===----------------------------------------------------------------------===//
#include "flang/Optimizer/CodeGen/CodeGenOpenMP.h"
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/LowLevelIntrinsics.h"
#include "flang/Optimizer/CodeGen/CodeGen.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/FIROps.h"
#include "flang/Optimizer/Dialect/FIRType.h"
#include "flang/Optimizer/Dialect/Support/FIRContext.h"
#include "flang/Optimizer/Support/FatalError.h"
#include "flang/Optimizer/Support/InternalNames.h"
#include "flang/Optimizer/Support/Utils.h"
#include "mlir/Conversion/LLVMCommon/ConversionTarget.h"
#include "mlir/Conversion/LLVMCommon/Pattern.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/Transforms/DialectConversion.h"
using namespace fir;
#define DEBUG_TYPE "flang-codegen-openmp"
// fir::LLVMTypeConverter for converting to LLVM IR dialect types.
#include "flang/Optimizer/CodeGen/TypeConverter.h"
namespace {
/// A pattern that converts the region arguments in a single-region OpenMP
/// operation to the LLVM dialect. The body of the region is not modified and is
/// expected to either be processed by the conversion infrastructure or already
/// contain ops compatible with LLVM dialect types.
template <typename OpType>
class OpenMPFIROpConversion : public mlir::ConvertOpToLLVMPattern<OpType> {
public:
explicit OpenMPFIROpConversion(const fir::LLVMTypeConverter &lowering)
: mlir::ConvertOpToLLVMPattern<OpType>(lowering) {}
const fir::LLVMTypeConverter &lowerTy() const {
return *static_cast<const fir::LLVMTypeConverter *>(
this->getTypeConverter());
}
};
// FIR Op specific conversion for MapInfoOp that overwrites the default OpenMP
// Dialect lowering, this allows FIR specific lowering of types, required for
// descriptors of allocatables currently.
struct MapInfoOpConversion
: public OpenMPFIROpConversion<mlir::omp::MapInfoOp> {
using OpenMPFIROpConversion::OpenMPFIROpConversion;
mlir::omp::MapBoundsOp
createBoundsForCharString(mlir::ConversionPatternRewriter &rewriter,
unsigned int len, mlir::Location loc) const {
mlir::Type i64Ty = rewriter.getIntegerType(64);
auto lBound = mlir::LLVM::ConstantOp::create(rewriter, loc, i64Ty, 0);
auto uBoundAndExt =
mlir::LLVM::ConstantOp::create(rewriter, loc, i64Ty, len - 1);
auto stride = mlir::LLVM::ConstantOp::create(rewriter, loc, i64Ty, 1);
auto baseLb = mlir::LLVM::ConstantOp::create(rewriter, loc, i64Ty, 1);
auto mapBoundType = rewriter.getType<mlir::omp::MapBoundsType>();
return mlir::omp::MapBoundsOp::create(rewriter, loc, mapBoundType, lBound,
uBoundAndExt, uBoundAndExt, stride,
/*strideInBytes*/ false, baseLb);
}
llvm::LogicalResult
matchAndRewrite(mlir::omp::MapInfoOp curOp, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
const mlir::TypeConverter *converter = getTypeConverter();
llvm::SmallVector<mlir::Type> resTypes;
if (failed(converter->convertTypes(curOp->getResultTypes(), resTypes)))
return mlir::failure();
llvm::SmallVector<mlir::NamedAttribute> newAttrs;
mlir::omp::MapBoundsOp mapBoundsOp;
for (mlir::NamedAttribute attr : curOp->getAttrs()) {
if (auto typeAttr = mlir::dyn_cast<mlir::TypeAttr>(attr.getValue())) {
mlir::Type newAttr;
if (fir::isTypeWithDescriptor(typeAttr.getValue())) {
newAttr = lowerTy().convertBoxTypeAsStruct(
mlir::cast<fir::BaseBoxType>(typeAttr.getValue()));
} else if (fir::isa_char_string(fir::unwrapSequenceType(
fir::unwrapPassByRefType(typeAttr.getValue()))) &&
!characterWithDynamicLen(
fir::unwrapPassByRefType(typeAttr.getValue()))) {
// Characters with a LEN param are represented as strings
// (array of characters), the lowering to LLVM dialect
// doesn't generate bounds for these (and this is not
// done at the initial lowering either) and there is
// minor inconsistencies in the variable types we
// create for the map without this step when converting
// to the LLVM dialect.
//
// For example, given the types:
//
// 1) CHARACTER(LEN=16), dimension(:,:), allocatable :: char_arr
// 2) CHARACTER(LEN=16), dimension(10,10) :: char_arr
//
// We get the FIR types (note for 1: we already peeled off the
// dynamic extents from the type at this stage, but the conversion
// to llvm dialect does that in any case, so the final result
// is the same):
//
// 1) !fir.char<1,16>
// 2) !fir.array<10x10x!fir.char<1,16>>
//
// Which are converted to the LLVM dialect types:
//
// 1) !llvm.array<16 x i8>
// 2) llvm.array<10 x array<10 x array<16 x i8>>
//
// And in both cases, we are missing the innermost bounds for
// the !fir.char<1,16> which is expanded into a 16 x i8 array
// in the conversion to LLVM dialect.
//
// The problem with this is that we would like to treat these
// cases identically and not have to create specialised
// lowerings for either of these in the lowering to LLVM-IR
// and treat them like any other array that passes through.
//
// To do so below, we generate an extra bound for the
// innermost array (the char type/string) using the LEN
// parameter of the character type. And we "canonicalize"
// the type, stripping it down to the base element type,
// which in this case is an i8. This effectively allows
// the lowering to treat this as a 1-D array with multiple
// bounds which it is capable of handling without any special
// casing.
// TODO: Handle dynamic LEN characters.
if (auto ct = mlir::dyn_cast_or_null<fir::CharacterType>(
fir::unwrapSequenceType(typeAttr.getValue()))) {
newAttr = converter->convertType(
fir::unwrapSequenceType(typeAttr.getValue()));
if (auto type = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(newAttr))
newAttr = type.getElementType();
// We do not generate MapBoundsOps for the device pass, as
// MapBoundsOps are not generated for the device pass, as
// they're unused in the device lowering.
auto offloadMod =
llvm::dyn_cast_or_null<mlir::omp::OffloadModuleInterface>(
*curOp->getParentOfType<mlir::ModuleOp>());
if (!offloadMod.getIsTargetDevice())
mapBoundsOp = createBoundsForCharString(rewriter, ct.getLen(),
curOp.getLoc());
} else {
newAttr = converter->convertType(typeAttr.getValue());
}
} else {
newAttr = converter->convertType(typeAttr.getValue());
}
newAttrs.emplace_back(attr.getName(), mlir::TypeAttr::get(newAttr));
} else {
newAttrs.push_back(attr);
}
}
auto newOp = rewriter.replaceOpWithNewOp<mlir::omp::MapInfoOp>(
curOp, resTypes, adaptor.getOperands(), newAttrs);
if (mapBoundsOp) {
rewriter.startOpModification(newOp);
newOp.getBoundsMutable().append(mlir::ValueRange{mapBoundsOp});
rewriter.finalizeOpModification(newOp);
}
return mlir::success();
}
};
// FIR op specific conversion for PrivateClauseOp that overwrites the default
// OpenMP Dialect lowering, this allows FIR-aware lowering of types, required
// for boxes because the OpenMP dialect conversion doesn't know anything about
// FIR types.
struct PrivateClauseOpConversion
: public OpenMPFIROpConversion<mlir::omp::PrivateClauseOp> {
using OpenMPFIROpConversion::OpenMPFIROpConversion;
llvm::LogicalResult
matchAndRewrite(mlir::omp::PrivateClauseOp curOp, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
const fir::LLVMTypeConverter &converter = lowerTy();
mlir::Type convertedAllocType;
if (auto box = mlir::dyn_cast<fir::BaseBoxType>(curOp.getType())) {
// In LLVM codegen fir.box<> == fir.ref<fir.box<>> == llvm.ptr
// Here we really do want the actual structure
if (box.isAssumedRank())
TODO(curOp->getLoc(), "Privatize an assumed rank array");
unsigned rank = 0;
if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(
fir::unwrapRefType(box.getEleTy())))
rank = seqTy.getShape().size();
convertedAllocType = converter.convertBoxTypeAsStruct(box, rank);
} else {
convertedAllocType = converter.convertType(adaptor.getType());
}
if (!convertedAllocType)
return mlir::failure();
rewriter.startOpModification(curOp);
curOp.setType(convertedAllocType);
rewriter.finalizeOpModification(curOp);
return mlir::success();
}
};
// Convert FIR type to LLVM without turning fir.box<T> into memory
// reference.
static mlir::Type convertObjectType(const fir::LLVMTypeConverter &converter,
mlir::Type firType) {
if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(firType))
return converter.convertBoxTypeAsStruct(boxTy);
return converter.convertType(firType);
}
// FIR Op specific conversion for TargetAllocMemOp
struct TargetAllocMemOpConversion
: public OpenMPFIROpConversion<mlir::omp::TargetAllocMemOp> {
using OpenMPFIROpConversion::OpenMPFIROpConversion;
llvm::LogicalResult
matchAndRewrite(mlir::omp::TargetAllocMemOp allocmemOp, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
mlir::Type heapTy = allocmemOp.getAllocatedType();
mlir::Location loc = allocmemOp.getLoc();
auto ity = lowerTy().indexType();
mlir::Type dataTy = fir::unwrapRefType(heapTy);
mlir::Type llvmObjectTy = convertObjectType(lowerTy(), dataTy);
if (fir::isRecordWithTypeParameters(fir::unwrapSequenceType(dataTy)))
TODO(loc, "omp.target_allocmem codegen of derived type with length "
"parameters");
mlir::Value size = fir::computeElementDistance(
loc, llvmObjectTy, ity, rewriter, lowerTy().getDataLayout());
if (auto scaleSize = fir::genAllocationScaleSize(
loc, allocmemOp.getInType(), ity, rewriter))
size = mlir::LLVM::MulOp::create(rewriter, loc, ity, size, scaleSize);
for (mlir::Value opnd : adaptor.getOperands().drop_front())
size = mlir::LLVM::MulOp::create(
rewriter, loc, ity, size,
integerCast(lowerTy(), loc, rewriter, ity, opnd));
auto mallocTyWidth = lowerTy().getIndexTypeBitwidth();
auto mallocTy =
mlir::IntegerType::get(rewriter.getContext(), mallocTyWidth);
if (mallocTyWidth != ity.getIntOrFloatBitWidth())
size = integerCast(lowerTy(), loc, rewriter, mallocTy, size);
rewriter.modifyOpInPlace(allocmemOp, [&]() {
allocmemOp.setInType(rewriter.getI8Type());
allocmemOp.getTypeparamsMutable().clear();
allocmemOp.getTypeparamsMutable().append(size);
});
return mlir::success();
}
};
struct DeclareMapperOpConversion
: public OpenMPFIROpConversion<mlir::omp::DeclareMapperOp> {
using OpenMPFIROpConversion::OpenMPFIROpConversion;
llvm::LogicalResult
matchAndRewrite(mlir::omp::DeclareMapperOp curOp, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
rewriter.startOpModification(curOp);
curOp.setType(convertObjectType(lowerTy(), curOp.getType()));
rewriter.finalizeOpModification(curOp);
return mlir::success();
}
};
} // namespace
void fir::populateOpenMPFIRToLLVMConversionPatterns(
const LLVMTypeConverter &converter, mlir::RewritePatternSet &patterns) {
patterns.add<MapInfoOpConversion>(converter);
patterns.add<PrivateClauseOpConversion>(converter);
patterns.add<TargetAllocMemOpConversion>(converter);
patterns.add<DeclareMapperOpConversion>(converter);
}