blob: f6f7be139dd6744cc60624b8db8f746215cf06e4 [file] [log] [blame]
//===- OpenACCToLLVMIRTranslation.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
//
//===----------------------------------------------------------------------===//
//
// This file implements a translation between the MLIR OpenACC dialect and LLVM
// IR.
//
//===----------------------------------------------------------------------===//
#include "mlir/Target/LLVMIR/Dialect/OpenACC/OpenACCToLLVMIRTranslation.h"
#include "mlir/Conversion/OpenACCToLLVM/ConvertOpenACCToLLVM.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Dialect/OpenACC/OpenACC.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Operation.h"
#include "mlir/Support/LLVM.h"
#include "mlir/Target/LLVMIR/ModuleTranslation.h"
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/Frontend/OpenMP/OMPConstants.h"
#include "llvm/Support/FormatVariadic.h"
using namespace mlir;
using OpenACCIRBuilder = llvm::OpenMPIRBuilder;
//===----------------------------------------------------------------------===//
// Utility functions
//===----------------------------------------------------------------------===//
/// Flag values are extracted from openmp/libomptarget/include/omptarget.h and
/// mapped to corresponding OpenACC flags.
static constexpr uint64_t kCreateFlag = 0x000;
static constexpr uint64_t kDeviceCopyinFlag = 0x001;
static constexpr uint64_t kHostCopyoutFlag = 0x002;
static constexpr uint64_t kCopyFlag = kDeviceCopyinFlag | kHostCopyoutFlag;
static constexpr uint64_t kPresentFlag = 0x1000;
static constexpr uint64_t kDeleteFlag = 0x008;
// Runtime extension to implement the OpenACC second reference counter.
static constexpr uint64_t kHoldFlag = 0x2000;
/// Default value for the device id
static constexpr int64_t kDefaultDevice = -1;
/// Create a constant string location from the MLIR Location information.
static llvm::Constant *createSourceLocStrFromLocation(Location loc,
OpenACCIRBuilder &builder,
StringRef name) {
if (auto fileLoc = loc.dyn_cast<FileLineColLoc>()) {
StringRef fileName = fileLoc.getFilename();
unsigned lineNo = fileLoc.getLine();
unsigned colNo = fileLoc.getColumn();
return builder.getOrCreateSrcLocStr(name, fileName, lineNo, colNo);
} else {
std::string locStr;
llvm::raw_string_ostream locOS(locStr);
locOS << loc;
return builder.getOrCreateSrcLocStr(locOS.str());
}
}
/// Create the location struct from the operation location information.
static llvm::Value *createSourceLocationInfo(OpenACCIRBuilder &builder,
Operation *op) {
auto loc = op->getLoc();
auto funcOp = op->getParentOfType<LLVM::LLVMFuncOp>();
StringRef funcName = funcOp ? funcOp.getName() : "unknown";
llvm::Constant *locStr =
createSourceLocStrFromLocation(loc, builder, funcName);
return builder.getOrCreateIdent(locStr);
}
/// Create a constant string representing the mapping information extracted from
/// the MLIR location information.
static llvm::Constant *createMappingInformation(Location loc,
OpenACCIRBuilder &builder) {
if (auto nameLoc = loc.dyn_cast<NameLoc>()) {
StringRef name = nameLoc.getName();
return createSourceLocStrFromLocation(nameLoc.getChildLoc(), builder, name);
} else {
return createSourceLocStrFromLocation(loc, builder, "unknown");
}
}
/// Return the runtime function used to lower the given operation.
static llvm::Function *getAssociatedFunction(OpenACCIRBuilder &builder,
Operation *op) {
return llvm::TypeSwitch<Operation *, llvm::Function *>(op)
.Case([&](acc::EnterDataOp) {
return builder.getOrCreateRuntimeFunctionPtr(
llvm::omp::OMPRTL___tgt_target_data_begin_mapper);
})
.Case([&](acc::ExitDataOp) {
return builder.getOrCreateRuntimeFunctionPtr(
llvm::omp::OMPRTL___tgt_target_data_end_mapper);
})
.Case([&](acc::UpdateOp) {
return builder.getOrCreateRuntimeFunctionPtr(
llvm::omp::OMPRTL___tgt_target_data_update_mapper);
});
llvm_unreachable("Unknown OpenACC operation");
}
/// Computes the size of type in bytes.
static llvm::Value *getSizeInBytes(llvm::IRBuilderBase &builder,
llvm::Value *basePtr) {
llvm::LLVMContext &ctx = builder.getContext();
llvm::Value *null =
llvm::Constant::getNullValue(basePtr->getType()->getPointerTo());
llvm::Value *sizeGep =
builder.CreateGEP(basePtr->getType(), null, builder.getInt32(1));
llvm::Value *sizePtrToInt =
builder.CreatePtrToInt(sizeGep, llvm::Type::getInt64Ty(ctx));
return sizePtrToInt;
}
/// Extract pointer, size and mapping information from operands
/// to populate the future functions arguments.
static LogicalResult
processOperands(llvm::IRBuilderBase &builder,
LLVM::ModuleTranslation &moduleTranslation, Operation *op,
ValueRange operands, unsigned totalNbOperand,
uint64_t operandFlag, SmallVector<uint64_t> &flags,
SmallVectorImpl<llvm::Constant *> &names, unsigned &index,
struct OpenACCIRBuilder::MapperAllocas &mapperAllocas) {
OpenACCIRBuilder *accBuilder = moduleTranslation.getOpenMPBuilder();
llvm::LLVMContext &ctx = builder.getContext();
auto *i8PtrTy = llvm::Type::getInt8PtrTy(ctx);
auto *arrI8PtrTy = llvm::ArrayType::get(i8PtrTy, totalNbOperand);
auto *i64Ty = llvm::Type::getInt64Ty(ctx);
auto *arrI64Ty = llvm::ArrayType::get(i64Ty, totalNbOperand);
for (Value data : operands) {
llvm::Value *dataValue = moduleTranslation.lookupValue(data);
llvm::Value *dataPtrBase;
llvm::Value *dataPtr;
llvm::Value *dataSize;
// Handle operands that were converted to DataDescriptor.
if (DataDescriptor::isValid(data)) {
dataPtrBase =
builder.CreateExtractValue(dataValue, kPtrBasePosInDataDescriptor);
dataPtr = builder.CreateExtractValue(dataValue, kPtrPosInDataDescriptor);
dataSize =
builder.CreateExtractValue(dataValue, kSizePosInDataDescriptor);
} else if (data.getType().isa<LLVM::LLVMPointerType>()) {
dataPtrBase = dataValue;
dataPtr = dataValue;
dataSize = getSizeInBytes(builder, dataValue);
} else {
return op->emitOpError()
<< "Data operand must be legalized before translation."
<< "Unsupported type: " << data.getType();
}
// Store base pointer extracted from operand into the i-th position of
// argBase.
llvm::Value *ptrBaseGEP = builder.CreateInBoundsGEP(
arrI8PtrTy, mapperAllocas.ArgsBase,
{builder.getInt32(0), builder.getInt32(index)});
llvm::Value *ptrBaseCast = builder.CreateBitCast(
ptrBaseGEP, dataPtrBase->getType()->getPointerTo());
builder.CreateStore(dataPtrBase, ptrBaseCast);
// Store pointer extracted from operand into the i-th position of args.
llvm::Value *ptrGEP = builder.CreateInBoundsGEP(
arrI8PtrTy, mapperAllocas.Args,
{builder.getInt32(0), builder.getInt32(index)});
llvm::Value *ptrCast =
builder.CreateBitCast(ptrGEP, dataPtr->getType()->getPointerTo());
builder.CreateStore(dataPtr, ptrCast);
// Store size extracted from operand into the i-th position of argSizes.
llvm::Value *sizeGEP = builder.CreateInBoundsGEP(
arrI64Ty, mapperAllocas.ArgSizes,
{builder.getInt32(0), builder.getInt32(index)});
builder.CreateStore(dataSize, sizeGEP);
flags.push_back(operandFlag);
llvm::Constant *mapName =
createMappingInformation(data.getLoc(), *accBuilder);
names.push_back(mapName);
++index;
}
return success();
}
/// Process data operands from acc::EnterDataOp
static LogicalResult
processDataOperands(llvm::IRBuilderBase &builder,
LLVM::ModuleTranslation &moduleTranslation,
acc::EnterDataOp op, SmallVector<uint64_t> &flags,
SmallVectorImpl<llvm::Constant *> &names,
struct OpenACCIRBuilder::MapperAllocas &mapperAllocas) {
// TODO add `create_zero` and `attach` operands
unsigned index = 0;
// Create operands are handled as `alloc` call.
if (failed(processOperands(builder, moduleTranslation, op,
op.createOperands(), op.getNumDataOperands(),
kCreateFlag, flags, names, index, mapperAllocas)))
return failure();
// Copyin operands are handled as `to` call.
if (failed(processOperands(builder, moduleTranslation, op,
op.copyinOperands(), op.getNumDataOperands(),
kDeviceCopyinFlag, flags, names, index,
mapperAllocas)))
return failure();
return success();
}
/// Process data operands from acc::ExitDataOp
static LogicalResult
processDataOperands(llvm::IRBuilderBase &builder,
LLVM::ModuleTranslation &moduleTranslation,
acc::ExitDataOp op, SmallVector<uint64_t> &flags,
SmallVectorImpl<llvm::Constant *> &names,
struct OpenACCIRBuilder::MapperAllocas &mapperAllocas) {
// TODO add `detach` operands
unsigned index = 0;
// Delete operands are handled as `delete` call.
if (failed(processOperands(builder, moduleTranslation, op,
op.deleteOperands(), op.getNumDataOperands(),
kDeleteFlag, flags, names, index, mapperAllocas)))
return failure();
// Copyout operands are handled as `from` call.
if (failed(processOperands(builder, moduleTranslation, op,
op.copyoutOperands(), op.getNumDataOperands(),
kHostCopyoutFlag, flags, names, index,
mapperAllocas)))
return failure();
return success();
}
/// Process data operands from acc::UpdateOp
static LogicalResult
processDataOperands(llvm::IRBuilderBase &builder,
LLVM::ModuleTranslation &moduleTranslation,
acc::UpdateOp op, SmallVector<uint64_t> &flags,
SmallVectorImpl<llvm::Constant *> &names,
struct OpenACCIRBuilder::MapperAllocas &mapperAllocas) {
unsigned index = 0;
// Host operands are handled as `from` call.
if (failed(processOperands(builder, moduleTranslation, op, op.hostOperands(),
op.getNumDataOperands(), kHostCopyoutFlag, flags,
names, index, mapperAllocas)))
return failure();
// Device operands are handled as `to` call.
if (failed(processOperands(builder, moduleTranslation, op,
op.deviceOperands(), op.getNumDataOperands(),
kDeviceCopyinFlag, flags, names, index,
mapperAllocas)))
return failure();
return success();
}
//===----------------------------------------------------------------------===//
// Conversion functions
//===----------------------------------------------------------------------===//
/// Converts an OpenACC data operation into LLVM IR.
static LogicalResult convertDataOp(acc::DataOp &op,
llvm::IRBuilderBase &builder,
LLVM::ModuleTranslation &moduleTranslation) {
llvm::LLVMContext &ctx = builder.getContext();
auto enclosingFuncOp = op.getOperation()->getParentOfType<LLVM::LLVMFuncOp>();
llvm::Function *enclosingFunction =
moduleTranslation.lookupFunction(enclosingFuncOp.getName());
OpenACCIRBuilder *accBuilder = moduleTranslation.getOpenMPBuilder();
llvm::Value *srcLocInfo = createSourceLocationInfo(*accBuilder, op);
llvm::Function *beginMapperFunc = accBuilder->getOrCreateRuntimeFunctionPtr(
llvm::omp::OMPRTL___tgt_target_data_begin_mapper);
llvm::Function *endMapperFunc = accBuilder->getOrCreateRuntimeFunctionPtr(
llvm::omp::OMPRTL___tgt_target_data_end_mapper);
// Number of arguments in the data operation.
unsigned totalNbOperand = op.getNumDataOperands();
struct OpenACCIRBuilder::MapperAllocas mapperAllocas;
OpenACCIRBuilder::InsertPointTy allocaIP(
&enclosingFunction->getEntryBlock(),
enclosingFunction->getEntryBlock().getFirstInsertionPt());
accBuilder->createMapperAllocas(builder.saveIP(), allocaIP, totalNbOperand,
mapperAllocas);
SmallVector<uint64_t> flags;
SmallVector<llvm::Constant *> names;
unsigned index = 0;
// TODO handle no_create, deviceptr and attach operands.
if (failed(processOperands(builder, moduleTranslation, op, op.copyOperands(),
totalNbOperand, kCopyFlag | kHoldFlag, flags,
names, index, mapperAllocas)))
return failure();
if (failed(processOperands(
builder, moduleTranslation, op, op.copyinOperands(), totalNbOperand,
kDeviceCopyinFlag | kHoldFlag, flags, names, index, mapperAllocas)))
return failure();
// TODO copyin readonly currenlty handled as copyin. Update when extension
// available.
if (failed(processOperands(builder, moduleTranslation, op,
op.copyinReadonlyOperands(), totalNbOperand,
kDeviceCopyinFlag | kHoldFlag, flags, names, index,
mapperAllocas)))
return failure();
if (failed(processOperands(
builder, moduleTranslation, op, op.copyoutOperands(), totalNbOperand,
kHostCopyoutFlag | kHoldFlag, flags, names, index, mapperAllocas)))
return failure();
// TODO copyout zero currenlty handled as copyout. Update when extension
// available.
if (failed(processOperands(builder, moduleTranslation, op,
op.copyoutZeroOperands(), totalNbOperand,
kHostCopyoutFlag | kHoldFlag, flags, names, index,
mapperAllocas)))
return failure();
if (failed(processOperands(builder, moduleTranslation, op,
op.createOperands(), totalNbOperand,
kCreateFlag | kHoldFlag, flags, names, index,
mapperAllocas)))
return failure();
// TODO create zero currenlty handled as create. Update when extension
// available.
if (failed(processOperands(builder, moduleTranslation, op,
op.createZeroOperands(), totalNbOperand,
kCreateFlag | kHoldFlag, flags, names, index,
mapperAllocas)))
return failure();
if (failed(processOperands(builder, moduleTranslation, op,
op.presentOperands(), totalNbOperand,
kPresentFlag | kHoldFlag, flags, names, index,
mapperAllocas)))
return failure();
llvm::GlobalVariable *maptypes =
accBuilder->createOffloadMaptypes(flags, ".offload_maptypes");
llvm::Value *maptypesArg = builder.CreateConstInBoundsGEP2_32(
llvm::ArrayType::get(llvm::Type::getInt64Ty(ctx), totalNbOperand),
maptypes, /*Idx0=*/0, /*Idx1=*/0);
llvm::GlobalVariable *mapnames =
accBuilder->createOffloadMapnames(names, ".offload_mapnames");
llvm::Value *mapnamesArg = builder.CreateConstInBoundsGEP2_32(
llvm::ArrayType::get(llvm::Type::getInt8PtrTy(ctx), totalNbOperand),
mapnames, /*Idx0=*/0, /*Idx1=*/0);
// Create call to start the data region.
accBuilder->emitMapperCall(builder.saveIP(), beginMapperFunc, srcLocInfo,
maptypesArg, mapnamesArg, mapperAllocas,
kDefaultDevice, totalNbOperand);
// Convert the region.
llvm::BasicBlock *entryBlock = nullptr;
for (Block &bb : op.region()) {
llvm::BasicBlock *llvmBB = llvm::BasicBlock::Create(
ctx, "acc.data", builder.GetInsertBlock()->getParent());
if (entryBlock == nullptr)
entryBlock = llvmBB;
moduleTranslation.mapBlock(&bb, llvmBB);
}
auto afterDataRegion = builder.saveIP();
llvm::BranchInst *sourceTerminator = builder.CreateBr(entryBlock);
builder.restoreIP(afterDataRegion);
llvm::BasicBlock *endDataBlock = llvm::BasicBlock::Create(
ctx, "acc.end_data", builder.GetInsertBlock()->getParent());
SetVector<Block *> blocks =
LLVM::detail::getTopologicallySortedBlocks(op.region());
for (Block *bb : blocks) {
llvm::BasicBlock *llvmBB = moduleTranslation.lookupBlock(bb);
if (bb->isEntryBlock()) {
assert(sourceTerminator->getNumSuccessors() == 1 &&
"provided entry block has multiple successors");
sourceTerminator->setSuccessor(0, llvmBB);
}
if (failed(
moduleTranslation.convertBlock(*bb, bb->isEntryBlock(), builder))) {
return failure();
}
if (isa<acc::TerminatorOp, acc::YieldOp>(bb->getTerminator()))
builder.CreateBr(endDataBlock);
}
// Create call to end the data region.
builder.SetInsertPoint(endDataBlock);
accBuilder->emitMapperCall(builder.saveIP(), endMapperFunc, srcLocInfo,
maptypesArg, mapnamesArg, mapperAllocas,
kDefaultDevice, totalNbOperand);
return success();
}
/// Converts an OpenACC standalone data operation into LLVM IR.
template <typename OpTy>
static LogicalResult
convertStandaloneDataOp(OpTy &op, llvm::IRBuilderBase &builder,
LLVM::ModuleTranslation &moduleTranslation) {
auto enclosingFuncOp =
op.getOperation()->template getParentOfType<LLVM::LLVMFuncOp>();
llvm::Function *enclosingFunction =
moduleTranslation.lookupFunction(enclosingFuncOp.getName());
OpenACCIRBuilder *accBuilder = moduleTranslation.getOpenMPBuilder();
auto *srcLocInfo = createSourceLocationInfo(*accBuilder, op);
auto *mapperFunc = getAssociatedFunction(*accBuilder, op);
// Number of arguments in the enter_data operation.
unsigned totalNbOperand = op.getNumDataOperands();
llvm::LLVMContext &ctx = builder.getContext();
struct OpenACCIRBuilder::MapperAllocas mapperAllocas;
OpenACCIRBuilder::InsertPointTy allocaIP(
&enclosingFunction->getEntryBlock(),
enclosingFunction->getEntryBlock().getFirstInsertionPt());
accBuilder->createMapperAllocas(builder.saveIP(), allocaIP, totalNbOperand,
mapperAllocas);
SmallVector<uint64_t> flags;
SmallVector<llvm::Constant *> names;
if (failed(processDataOperands(builder, moduleTranslation, op, flags, names,
mapperAllocas)))
return failure();
llvm::GlobalVariable *maptypes =
accBuilder->createOffloadMaptypes(flags, ".offload_maptypes");
llvm::Value *maptypesArg = builder.CreateConstInBoundsGEP2_32(
llvm::ArrayType::get(llvm::Type::getInt64Ty(ctx), totalNbOperand),
maptypes, /*Idx0=*/0, /*Idx1=*/0);
llvm::GlobalVariable *mapnames =
accBuilder->createOffloadMapnames(names, ".offload_mapnames");
llvm::Value *mapnamesArg = builder.CreateConstInBoundsGEP2_32(
llvm::ArrayType::get(llvm::Type::getInt8PtrTy(ctx), totalNbOperand),
mapnames, /*Idx0=*/0, /*Idx1=*/0);
accBuilder->emitMapperCall(builder.saveIP(), mapperFunc, srcLocInfo,
maptypesArg, mapnamesArg, mapperAllocas,
kDefaultDevice, totalNbOperand);
return success();
}
namespace {
/// Implementation of the dialect interface that converts operations belonging
/// to the OpenACC dialect to LLVM IR.
class OpenACCDialectLLVMIRTranslationInterface
: public LLVMTranslationDialectInterface {
public:
using LLVMTranslationDialectInterface::LLVMTranslationDialectInterface;
/// Translates the given operation to LLVM IR using the provided IR builder
/// and saving the state in `moduleTranslation`.
LogicalResult
convertOperation(Operation *op, llvm::IRBuilderBase &builder,
LLVM::ModuleTranslation &moduleTranslation) const final;
};
} // end namespace
/// Given an OpenACC MLIR operation, create the corresponding LLVM IR
/// (including OpenACC runtime calls).
LogicalResult OpenACCDialectLLVMIRTranslationInterface::convertOperation(
Operation *op, llvm::IRBuilderBase &builder,
LLVM::ModuleTranslation &moduleTranslation) const {
return llvm::TypeSwitch<Operation *, LogicalResult>(op)
.Case([&](acc::DataOp dataOp) {
return convertDataOp(dataOp, builder, moduleTranslation);
})
.Case([&](acc::EnterDataOp enterDataOp) {
return convertStandaloneDataOp<acc::EnterDataOp>(enterDataOp, builder,
moduleTranslation);
})
.Case([&](acc::ExitDataOp exitDataOp) {
return convertStandaloneDataOp<acc::ExitDataOp>(exitDataOp, builder,
moduleTranslation);
})
.Case([&](acc::UpdateOp updateOp) {
return convertStandaloneDataOp<acc::UpdateOp>(updateOp, builder,
moduleTranslation);
})
.Case<acc::TerminatorOp, acc::YieldOp>([](auto op) {
// `yield` and `terminator` can be just omitted. The block structure was
// created in the function that handles their parent operation.
assert(op->getNumOperands() == 0 &&
"unexpected OpenACC terminator with operands");
return success();
})
.Default([&](Operation *op) {
return op->emitError("unsupported OpenACC operation: ")
<< op->getName();
});
}
void mlir::registerOpenACCDialectTranslation(DialectRegistry &registry) {
registry.insert<acc::OpenACCDialect>();
registry.addDialectInterface<acc::OpenACCDialect,
OpenACCDialectLLVMIRTranslationInterface>();
}
void mlir::registerOpenACCDialectTranslation(MLIRContext &context) {
DialectRegistry registry;
registerOpenACCDialectTranslation(registry);
context.appendDialectRegistry(registry);
}