--- /dev/null
+//===- 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
+//===----------------------------------------------------------------------===//
+
+/// 0 = alloc/create
+static constexpr uint64_t createFlag = 0;
+/// 1 = to/copyin
+static constexpr uint64_t copyinFlag = 1;
+/// Default value for the device id
+static constexpr int64_t defaultDevice = -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(acc::EnterDataOp &op,
+ OpenACCIRBuilder &builder) {
+ auto loc = op.getLoc();
+ auto funcOp = op.getOperation()->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) {
+ if (isa<acc::EnterDataOp>(op))
+ return builder.getOrCreateRuntimeFunctionPtr(
+ llvm::omp::OMPRTL___tgt_target_data_begin_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,
+ SmallVector<llvm::Constant *> &names, unsigned &index,
+ llvm::AllocaInst *argsBase, llvm::AllocaInst *args,
+ llvm::AllocaInst *argSizes) {
+ 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, 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, 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, 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();
+}
+
+//===----------------------------------------------------------------------===//
+// Conversion functions
+//===----------------------------------------------------------------------===//
+
+/// Converts an OpenACC enter_data operartion into LLVM IR.
+static LogicalResult
+convertEnterDataOp(Operation &op, llvm::IRBuilderBase &builder,
+ LLVM::ModuleTranslation &moduleTranslation) {
+ auto enterDataOp = cast<acc::EnterDataOp>(op);
+ auto enclosingFuncOp = op.getParentOfType<LLVM::LLVMFuncOp>();
+ llvm::Function *enclosingFunction =
+ moduleTranslation.lookupFunction(enclosingFuncOp.getName());
+
+ OpenACCIRBuilder *accBuilder = moduleTranslation.getOpenMPBuilder();
+
+ auto *srcLocInfo = createSourceLocationInfo(enterDataOp, *accBuilder);
+ auto *mapperFunc = getAssociatedFunction(*accBuilder, op);
+
+ // Number of arguments in the enter_data operation.
+ // TODO include create_zero and attach operands.
+ unsigned totalNbOperand =
+ enterDataOp.createOperands().size() + enterDataOp.copyinOperands().size();
+
+ // TODO could be moved to OpenXXIRBuilder?
+ 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);
+ llvm::IRBuilder<>::InsertPoint allocaIP(
+ &enclosingFunction->getEntryBlock(),
+ enclosingFunction->getEntryBlock().getFirstInsertionPt());
+ llvm::IRBuilder<>::InsertPoint currentIP = builder.saveIP();
+ builder.restoreIP(allocaIP);
+ llvm::AllocaInst *argsBase = builder.CreateAlloca(arrI8PtrTy);
+ llvm::AllocaInst *args = builder.CreateAlloca(arrI8PtrTy);
+ llvm::AllocaInst *argSizes = builder.CreateAlloca(arrI64Ty);
+ builder.restoreIP(currentIP);
+
+ SmallVector<uint64_t> flags;
+ SmallVector<llvm::Constant *> names;
+ unsigned index = 0;
+
+ // Create operands are handled as `alloc` call.
+ if (failed(processOperands(builder, moduleTranslation, op,
+ enterDataOp.createOperands(), totalNbOperand,
+ createFlag, flags, names, index, argsBase, args,
+ argSizes)))
+ return failure();
+
+ // Copyin operands are handled as `to` call.
+ if (failed(processOperands(builder, moduleTranslation, op,
+ enterDataOp.copyinOperands(), totalNbOperand,
+ copyinFlag, flags, names, index, argsBase, args,
+ argSizes)))
+ 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);
+
+ llvm::Value *argsBaseGEP = builder.CreateInBoundsGEP(
+ arrI8PtrTy, argsBase, {builder.getInt32(0), builder.getInt32(0)});
+ llvm::Value *argsGEP = builder.CreateInBoundsGEP(
+ arrI8PtrTy, args, {builder.getInt32(0), builder.getInt32(0)});
+ llvm::Value *argSizesGEP = builder.CreateInBoundsGEP(
+ arrI64Ty, argSizes, {builder.getInt32(0), builder.getInt32(0)});
+ llvm::Value *nullPtr = llvm::Constant::getNullValue(
+ llvm::Type::getInt8PtrTy(ctx)->getPointerTo());
+
+ builder.CreateCall(mapperFunc,
+ {srcLocInfo, builder.getInt64(defaultDevice),
+ builder.getInt32(totalNbOperand), argsBaseGEP, argsGEP,
+ argSizesGEP, maptypesArg, mapnamesArg, nullPtr});
+
+ 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::EnterDataOp) {
+ return convertEnterDataOp(*op, builder, moduleTranslation);
+ })
+ .Default([&](Operation *op) {
+ return op->emitError("unsupported OpenACC operation: ")
+ << op->getName();
+ });
+}
+
+void mlir::registerOpenACCDialectTranslation(DialectRegistry ®istry) {
+ registry.insert<acc::OpenACCDialect>();
+ registry.addDialectInterface<acc::OpenACCDialect,
+ OpenACCDialectLLVMIRTranslationInterface>();
+}
+
+void mlir::registerOpenACCDialectTranslation(MLIRContext &context) {
+ DialectRegistry registry;
+ registerOpenACCDialectTranslation(registry);
+ context.appendDialectRegistry(registry);
+}
--- /dev/null
+// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s
+
+llvm.func @testenterdataop(%arg0: !llvm.ptr<f32>, %arg1: !llvm.ptr<f32>, %arg2: i64, %arg3: i64, %arg4: i64, %arg5: !llvm.ptr<f32>) {
+ %0 = llvm.mlir.undef : !llvm.struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>
+ %1 = llvm.insertvalue %arg0, %0[0] : !llvm.struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>
+ %2 = llvm.insertvalue %arg1, %1[1] : !llvm.struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>
+ %3 = llvm.insertvalue %arg2, %2[2] : !llvm.struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>
+ %4 = llvm.insertvalue %arg3, %3[3, 0] : !llvm.struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>
+ %5 = llvm.insertvalue %arg4, %4[4, 0] : !llvm.struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>
+ %6 = llvm.mlir.constant(10 : index) : i64
+ %7 = llvm.mlir.constant(1 : index) : i64
+ %8 = llvm.mlir.null : !llvm.ptr<f32>
+ %9 = llvm.getelementptr %8[%6] : (!llvm.ptr<f32>, i64) -> !llvm.ptr<f32>
+ %10 = llvm.ptrtoint %9 : !llvm.ptr<f32> to i64
+ %11 = llvm.extractvalue %5[1] : !llvm.struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>
+ %12 = llvm.mlir.undef : !llvm.struct<"openacc_data", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>
+ %13 = llvm.insertvalue %5, %12[0] : !llvm.struct<"openacc_data", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>
+ %14 = llvm.insertvalue %11, %13[1] : !llvm.struct<"openacc_data", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>
+ %15 = llvm.insertvalue %10, %14[2] : !llvm.struct<"openacc_data", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>
+ acc.enter_data copyin(%arg5 : !llvm.ptr<f32>) create(%15 : !llvm.struct<"openacc_data", (struct<(ptr<f32>, ptr<f32>, i64, array<1 x i64>, array<1 x i64>)>, ptr<f32>, i64)>)
+ llvm.return
+}
+
+// CHECK: %struct.ident_t = type { i32, i32, i32, i32, i8* }
+
+// CHECK: [[LOCSTR:@.*]] = private unnamed_addr constant [{{[0-9]*}} x i8] c";{{.*}};testenterdataop;{{[0-9]*}};{{[0-9]*}};;\00", align 1
+// CHECK: [[LOCGLOBAL:@.*]] = private unnamed_addr constant %struct.ident_t { i32 0, i32 2, i32 0, i32 0, i8* getelementptr inbounds ([{{[0-9]*}} x i8], [{{[0-9]*}} x i8]* [[LOCSTR]], i32 0, i32 0) }, align 8
+// CHECK: [[MAPNAME1:@.*]] = private unnamed_addr constant [{{[0-9]*}} x i8] c";{{.*}};unknown;{{[0-9]*}};{{[0-9]*}};;\00", align 1
+// CHECK: [[MAPNAME2:@.*]] = private unnamed_addr constant [{{[0-9]*}} x i8] c";{{.*}};unknown;{{[0-9]*}};{{[0-9]*}};;\00", align 1
+// CHECK: [[MAPTYPES:@.*]] = private unnamed_addr constant [{{[0-9]*}} x i64] [i64 0, i64 1]
+// CHECK: [[MAPNAMES:@.*]] = private constant [{{[0-9]*}} x i8*] [i8* getelementptr inbounds ([{{[0-9]*}} x i8], [{{[0-9]*}} x i8]* [[MAPNAME1]], i32 0, i32 0), i8* getelementptr inbounds ([{{[0-9]*}} x i8], [{{[0-9]*}} x i8]* [[MAPNAME2]], i32 0, i32 0)]
+
+// CHECK: define void @testenterdataop(float* %{{.*}}, float* %{{.*}}, i64 %{{.*}}, i64 %{{.*}}, i64 %{{.*}}, float* [[SIMPLEPTR:%.*]])
+// CHECK: [[ARGBASE_ALLOCA:%.*]] = alloca [{{[0-9]*}} x i8*], align 8
+// CHECK: [[ARG_ALLOCA:%.*]] = alloca [{{[0-9]*}} x i8*], align 8
+// CHECK: [[SIZE_ALLOCA:%.*]] = alloca [{{[0-9]*}} x i64], align 8
+
+// CHECK: [[ARGBASE:%.*]] = extractvalue %openacc_data %{{.*}}, 0
+// CHECK: [[ARG:%.*]] = extractvalue %openacc_data %{{.*}}, 1
+// CHECK: [[ARGSIZE:%.*]] = extractvalue %openacc_data %{{.*}}, 2
+// CHECK: [[ARGBASEGEP:%.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* [[ARGBASE_ALLOCA]], i32 0, i32 0
+// CHECK: [[ARGBASEGEPCAST:%.*]] = bitcast i8** [[ARGBASEGEP]] to { float*, float*, i64, [1 x i64], [1 x i64] }*
+// CHECK: store { float*, float*, i64, [1 x i64], [1 x i64] } [[ARGBASE]], { float*, float*, i64, [1 x i64], [1 x i64] }* [[ARGBASEGEPCAST]], align 8
+// CHECK: [[ARGGEP:%.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* [[ARG_ALLOCA]], i32 0, i32 0
+// CHECK: [[ARGGEPCAST:%.*]] = bitcast i8** [[ARGGEP]] to float**
+// CHECK: store float* [[ARG]], float** [[ARGGEPCAST]], align 8
+// CHECK: [[SIZEGEP:%.*]] = getelementptr inbounds [2 x i64], [2 x i64]* [[SIZE_ALLOCA]], i32 0, i32 0
+// CHECK: store i64 [[ARGSIZE]], i64* [[SIZEGEP]], align 4
+
+// CHECK: [[ARGBASEGEP:%.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* [[ARGBASE_ALLOCA]], i32 0, i32 1
+// CHECK: [[ARGBASEGEPCAST:%.*]] = bitcast i8** [[ARGBASEGEP]] to float**
+// CHECK: store float* [[SIMPLEPTR]], float** [[ARGBASEGEPCAST]], align 8
+// CHECK: [[ARGGEP:%.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* [[ARG_ALLOCA]], i32 0, i32 1
+// CHECK: [[ARGGEPCAST:%.*]] = bitcast i8** [[ARGGEP]] to float**
+// CHECK: store float* [[SIMPLEPTR]], float** [[ARGGEPCAST]], align 8
+// CHECK: [[SIZEGEP:%.*]] = getelementptr inbounds [2 x i64], [2 x i64]* [[SIZE_ALLOCA]], i32 0, i32 1
+// CHECK: store i64 ptrtoint (i1** getelementptr (i1*, i1** null, i32 1) to i64), i64* [[SIZEGEP]], align 4
+
+// CHECK: [[ARGBASE_ALLOCA_GEP:%.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* [[ARGBASE_ALLOCA]], i32 0, i32 0
+// CHECK: [[ARG_ALLOCA_GEP:%.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* [[ARG_ALLOCA]], i32 0, i32 0
+// CHECK: [[SIZE_ALLOCA_GEP:%.*]] = getelementptr inbounds [2 x i64], [2 x i64]* [[SIZE_ALLOCA]], i32 0, i32 0
+
+// CHECK: call void @__tgt_target_data_begin_mapper(%struct.ident_t* [[LOCGLOBAL]], i64 -1, i32 2, i8** [[ARGBASE_ALLOCA_GEP]], i8** [[ARG_ALLOCA_GEP]], i64* [[SIZE_ALLOCA_GEP]], i64* getelementptr inbounds ([{{[0-9]*}} x i64], [{{[0-9]*}} x i64]* [[MAPTYPES]], i32 0, i32 0), i8** getelementptr inbounds ([{{[0-9]*}} x i8*], [{{[0-9]*}} x i8*]* [[MAPNAMES]], i32 0, i32 0), i8** null)
+
+// CHECK: declare void @__tgt_target_data_begin_mapper(%struct.ident_t*, i64, i32, i8**, i8**, i64*, i64*, i8**, i8**) #0