blob: d6dac4998fdcc5b3b8971285c91e8cddb739ee13 [file] [log] [blame]
//===-- CodeGen.cpp -- bridge to lower to LLVM ----------------------------===//
//
// 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/FIROpPatterns.h"
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
#include "llvm/Support/Debug.h"
static inline mlir::Type getLlvmPtrType(mlir::MLIRContext *context,
unsigned addressSpace = 0) {
return mlir::LLVM::LLVMPointerType::get(context, addressSpace);
}
static unsigned getTypeDescFieldId(mlir::Type ty) {
auto isArray = mlir::isa<fir::SequenceType>(fir::dyn_cast_ptrOrBoxEleTy(ty));
return isArray ? kOptTypePtrPosInBox : kDimsPosInBox;
}
namespace fir {
ConvertFIRToLLVMPattern::ConvertFIRToLLVMPattern(
llvm::StringRef rootOpName, mlir::MLIRContext *context,
const fir::LLVMTypeConverter &typeConverter,
const fir::FIRToLLVMPassOptions &options, mlir::PatternBenefit benefit)
: ConvertToLLVMPattern(rootOpName, context, typeConverter, benefit),
options(options) {}
// Convert FIR type to LLVM without turning fir.box<T> into memory
// reference.
mlir::Type
ConvertFIRToLLVMPattern::convertObjectType(mlir::Type firType) const {
if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(firType))
return lowerTy().convertBoxTypeAsStruct(boxTy);
return lowerTy().convertType(firType);
}
mlir::LLVM::ConstantOp ConvertFIRToLLVMPattern::genI32Constant(
mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
int value) const {
mlir::Type i32Ty = rewriter.getI32Type();
mlir::IntegerAttr attr = rewriter.getI32IntegerAttr(value);
return rewriter.create<mlir::LLVM::ConstantOp>(loc, i32Ty, attr);
}
mlir::LLVM::ConstantOp ConvertFIRToLLVMPattern::genConstantOffset(
mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
int offset) const {
mlir::Type ity = lowerTy().offsetType();
mlir::IntegerAttr cattr = rewriter.getI32IntegerAttr(offset);
return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
}
/// Perform an extension or truncation as needed on an integer value. Lowering
/// to the specific target may involve some sign-extending or truncation of
/// values, particularly to fit them from abstract box types to the
/// appropriate reified structures.
mlir::Value
ConvertFIRToLLVMPattern::integerCast(mlir::Location loc,
mlir::ConversionPatternRewriter &rewriter,
mlir::Type ty, mlir::Value val) const {
auto valTy = val.getType();
// If the value was not yet lowered, lower its type so that it can
// be used in getPrimitiveTypeSizeInBits.
if (!mlir::isa<mlir::IntegerType>(valTy))
valTy = convertType(valTy);
auto toSize = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
auto fromSize = mlir::LLVM::getPrimitiveTypeSizeInBits(valTy);
if (toSize < fromSize)
return rewriter.create<mlir::LLVM::TruncOp>(loc, ty, val);
if (toSize > fromSize)
return rewriter.create<mlir::LLVM::SExtOp>(loc, ty, val);
return val;
}
fir::ConvertFIRToLLVMPattern::TypePair
ConvertFIRToLLVMPattern::getBoxTypePair(mlir::Type firBoxTy) const {
mlir::Type llvmBoxTy =
lowerTy().convertBoxTypeAsStruct(mlir::cast<fir::BaseBoxType>(firBoxTy));
return TypePair{firBoxTy, llvmBoxTy};
}
/// Construct code sequence to extract the specific value from a `fir.box`.
mlir::Value ConvertFIRToLLVMPattern::getValueFromBox(
mlir::Location loc, TypePair boxTy, mlir::Value box, mlir::Type resultTy,
mlir::ConversionPatternRewriter &rewriter, int boxValue) const {
if (mlir::isa<mlir::LLVM::LLVMPointerType>(box.getType())) {
auto pty = getLlvmPtrType(resultTy.getContext());
auto p = rewriter.create<mlir::LLVM::GEPOp>(
loc, pty, boxTy.llvm, box,
llvm::ArrayRef<mlir::LLVM::GEPArg>{0, boxValue});
auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p);
attachTBAATag(loadOp, boxTy.fir, nullptr, p);
return loadOp;
}
return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, box, boxValue);
}
/// Method to construct code sequence to get the triple for dimension `dim`
/// from a box.
llvm::SmallVector<mlir::Value, 3> ConvertFIRToLLVMPattern::getDimsFromBox(
mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys, TypePair boxTy,
mlir::Value box, mlir::Value dim,
mlir::ConversionPatternRewriter &rewriter) const {
mlir::Value l0 =
loadDimFieldFromBox(loc, boxTy, box, dim, 0, retTys[0], rewriter);
mlir::Value l1 =
loadDimFieldFromBox(loc, boxTy, box, dim, 1, retTys[1], rewriter);
mlir::Value l2 =
loadDimFieldFromBox(loc, boxTy, box, dim, 2, retTys[2], rewriter);
return {l0, l1, l2};
}
llvm::SmallVector<mlir::Value, 3> ConvertFIRToLLVMPattern::getDimsFromBox(
mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys, TypePair boxTy,
mlir::Value box, int dim, mlir::ConversionPatternRewriter &rewriter) const {
mlir::Value l0 =
getDimFieldFromBox(loc, boxTy, box, dim, 0, retTys[0], rewriter);
mlir::Value l1 =
getDimFieldFromBox(loc, boxTy, box, dim, 1, retTys[1], rewriter);
mlir::Value l2 =
getDimFieldFromBox(loc, boxTy, box, dim, 2, retTys[2], rewriter);
return {l0, l1, l2};
}
mlir::Value ConvertFIRToLLVMPattern::loadDimFieldFromBox(
mlir::Location loc, TypePair boxTy, mlir::Value box, mlir::Value dim,
int off, mlir::Type ty, mlir::ConversionPatternRewriter &rewriter) const {
assert(mlir::isa<mlir::LLVM::LLVMPointerType>(box.getType()) &&
"descriptor inquiry with runtime dim can only be done on descriptor "
"in memory");
mlir::LLVM::GEPOp p = genGEP(loc, boxTy.llvm, rewriter, box, 0,
static_cast<int>(kDimsPosInBox), dim, off);
auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
attachTBAATag(loadOp, boxTy.fir, nullptr, p);
return loadOp;
}
mlir::Value ConvertFIRToLLVMPattern::getDimFieldFromBox(
mlir::Location loc, TypePair boxTy, mlir::Value box, int dim, int off,
mlir::Type ty, mlir::ConversionPatternRewriter &rewriter) const {
if (mlir::isa<mlir::LLVM::LLVMPointerType>(box.getType())) {
mlir::LLVM::GEPOp p = genGEP(loc, boxTy.llvm, rewriter, box, 0,
static_cast<int>(kDimsPosInBox), dim, off);
auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
attachTBAATag(loadOp, boxTy.fir, nullptr, p);
return loadOp;
}
return rewriter.create<mlir::LLVM::ExtractValueOp>(
loc, box, llvm::ArrayRef<std::int64_t>{kDimsPosInBox, dim, off});
}
mlir::Value ConvertFIRToLLVMPattern::getStrideFromBox(
mlir::Location loc, TypePair boxTy, mlir::Value box, unsigned dim,
mlir::ConversionPatternRewriter &rewriter) const {
auto idxTy = lowerTy().indexType();
return getDimFieldFromBox(loc, boxTy, box, dim, kDimStridePos, idxTy,
rewriter);
}
/// Read base address from a fir.box. Returned address has type ty.
mlir::Value ConvertFIRToLLVMPattern::getBaseAddrFromBox(
mlir::Location loc, TypePair boxTy, mlir::Value box,
mlir::ConversionPatternRewriter &rewriter) const {
mlir::Type resultTy = ::getLlvmPtrType(boxTy.llvm.getContext());
return getValueFromBox(loc, boxTy, box, resultTy, rewriter, kAddrPosInBox);
}
mlir::Value ConvertFIRToLLVMPattern::getElementSizeFromBox(
mlir::Location loc, mlir::Type resultTy, TypePair boxTy, mlir::Value box,
mlir::ConversionPatternRewriter &rewriter) const {
return getValueFromBox(loc, boxTy, box, resultTy, rewriter, kElemLenPosInBox);
}
// Get the element type given an LLVM type that is of the form
// (array|struct|vector)+ and the provided indexes.
mlir::Type ConvertFIRToLLVMPattern::getBoxEleTy(
mlir::Type type, llvm::ArrayRef<std::int64_t> indexes) const {
for (unsigned i : indexes) {
if (auto t = mlir::dyn_cast<mlir::LLVM::LLVMStructType>(type)) {
assert(!t.isOpaque() && i < t.getBody().size());
type = t.getBody()[i];
} else if (auto t = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(type)) {
type = t.getElementType();
} else if (auto t = mlir::dyn_cast<mlir::VectorType>(type)) {
type = t.getElementType();
} else {
fir::emitFatalError(mlir::UnknownLoc::get(type.getContext()),
"request for invalid box element type");
}
}
return type;
}
// Return LLVM type of the object described by a fir.box of \p boxType.
mlir::Type ConvertFIRToLLVMPattern::getLlvmObjectTypeFromBoxType(
mlir::Type boxType) const {
mlir::Type objectType = fir::dyn_cast_ptrOrBoxEleTy(boxType);
assert(objectType && "boxType must be a box type");
return this->convertType(objectType);
}
/// Read the address of the type descriptor from a box.
mlir::Value ConvertFIRToLLVMPattern::loadTypeDescAddress(
mlir::Location loc, TypePair boxTy, mlir::Value box,
mlir::ConversionPatternRewriter &rewriter) const {
unsigned typeDescFieldId = getTypeDescFieldId(boxTy.fir);
mlir::Type tdescType = lowerTy().convertTypeDescType(rewriter.getContext());
return getValueFromBox(loc, boxTy, box, tdescType, rewriter, typeDescFieldId);
}
// Load the attribute from the \p box and perform a check against \p maskValue
// The final comparison is implemented as `(attribute & maskValue) != 0`.
mlir::Value ConvertFIRToLLVMPattern::genBoxAttributeCheck(
mlir::Location loc, TypePair boxTy, mlir::Value box,
mlir::ConversionPatternRewriter &rewriter, unsigned maskValue) const {
mlir::Type attrTy = rewriter.getI32Type();
mlir::Value attribute =
getValueFromBox(loc, boxTy, box, attrTy, rewriter, kAttributePosInBox);
mlir::LLVM::ConstantOp attrMask = genConstantOffset(loc, rewriter, maskValue);
auto maskRes =
rewriter.create<mlir::LLVM::AndOp>(loc, attrTy, attribute, attrMask);
mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
return rewriter.create<mlir::LLVM::ICmpOp>(loc, mlir::LLVM::ICmpPredicate::ne,
maskRes, c0);
}
// Find the Block in which the alloca should be inserted.
// The order to recursively find the proper block:
// 1. An OpenMP Op that will be outlined.
// 2. A LLVMFuncOp
// 3. The first ancestor that is an OpenMP Op or a LLVMFuncOp
mlir::Block *
ConvertFIRToLLVMPattern::getBlockForAllocaInsert(mlir::Operation *op) const {
if (auto iface = mlir::dyn_cast<mlir::omp::OutlineableOpenMPOpInterface>(op))
return iface.getAllocaBlock();
if (auto llvmFuncOp = mlir::dyn_cast<mlir::LLVM::LLVMFuncOp>(op))
return &llvmFuncOp.front();
if (auto ompPrivateOp = mlir::dyn_cast<mlir::omp::PrivateClauseOp>(op))
return &ompPrivateOp.getAllocRegion().front();
return getBlockForAllocaInsert(op->getParentOp());
}
// Generate an alloca of size 1 for an object of type \p llvmObjectTy in the
// allocation address space provided for the architecture in the DataLayout
// specification. If the address space is different from the devices
// program address space we perform a cast. In the case of most architectures
// the program and allocation address space will be the default of 0 and no
// cast will be emitted.
mlir::Value ConvertFIRToLLVMPattern::genAllocaAndAddrCastWithType(
mlir::Location loc, mlir::Type llvmObjectTy, unsigned alignment,
mlir::ConversionPatternRewriter &rewriter) const {
auto thisPt = rewriter.saveInsertionPoint();
mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();
if (mlir::isa<mlir::omp::DeclareReductionOp>(parentOp)) {
// DeclareReductionOp has multiple child regions. We want to get the first
// block of whichever of those regions we are currently in
mlir::Region *parentRegion = rewriter.getInsertionBlock()->getParent();
rewriter.setInsertionPointToStart(&parentRegion->front());
} else {
mlir::Block *insertBlock = getBlockForAllocaInsert(parentOp);
rewriter.setInsertionPointToStart(insertBlock);
}
auto size = genI32Constant(loc, rewriter, 1);
unsigned allocaAs = getAllocaAddressSpace(rewriter);
unsigned programAs = getProgramAddressSpace(rewriter);
mlir::Value al = rewriter.create<mlir::LLVM::AllocaOp>(
loc, ::getLlvmPtrType(llvmObjectTy.getContext(), allocaAs), llvmObjectTy,
size, alignment);
// if our allocation address space, is not the same as the program address
// space, then we must emit a cast to the program address space before use.
// An example case would be on AMDGPU, where the allocation address space is
// the numeric value 5 (private), and the program address space is 0
// (generic).
if (allocaAs != programAs) {
al = rewriter.create<mlir::LLVM::AddrSpaceCastOp>(
loc, ::getLlvmPtrType(llvmObjectTy.getContext(), programAs), al);
}
rewriter.restoreInsertionPoint(thisPt);
return al;
}
unsigned ConvertFIRToLLVMPattern::getAllocaAddressSpace(
mlir::ConversionPatternRewriter &rewriter) const {
mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();
assert(parentOp != nullptr &&
"expected insertion block to have parent operation");
if (auto module = parentOp->getParentOfType<mlir::ModuleOp>())
if (mlir::Attribute addrSpace =
mlir::DataLayout(module).getAllocaMemorySpace())
return llvm::cast<mlir::IntegerAttr>(addrSpace).getUInt();
return defaultAddressSpace;
}
unsigned ConvertFIRToLLVMPattern::getProgramAddressSpace(
mlir::ConversionPatternRewriter &rewriter) const {
mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();
assert(parentOp != nullptr &&
"expected insertion block to have parent operation");
if (auto module = parentOp->getParentOfType<mlir::ModuleOp>())
if (mlir::Attribute addrSpace =
mlir::DataLayout(module).getProgramMemorySpace())
return llvm::cast<mlir::IntegerAttr>(addrSpace).getUInt();
return defaultAddressSpace;
}
} // namespace fir