[MLIR][OpenMP] Added OMPIRBuilder support for Target Data directives

This patch adds OpenMP IRBuilder support for the Target Data directives to allow lowering to LLVM IR.

The mlir::Translation is responsible for generating supporting code for processing the map_operands through the processMapOperand function, and also generate code for the r>

The OMPIRBuilder is responsible for generating the begin and end mapper function calls.

Limitations:
        - use_device_ptr and use_device_addr clauses are NOT supported for Target Data operation.
        - nowait clauses are NOT supported for Target Enter and Exit Data operations.
        - Only LLVMPointerType is supported for map_operands.

Differential Revision: https://reviews.llvm.org/D142914
This commit is contained in:
Akash Banerjee 2023-03-17 12:40:29 +00:00
parent 92416b63a5
commit 2d373e4dc7
11 changed files with 814 additions and 55 deletions

View File

@ -498,11 +498,6 @@ enum OpenMPOffloadingRequiresDirFlags : int64_t {
LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/OMP_REQ_DYNAMIC_ALLOCATORS)
};
enum OpenMPOffloadingReservedDeviceIDs {
/// Device ID if the device was not defined, runtime should get it
/// from environment variables in the spec.
OMP_DEVICEID_UNDEF = -1,
};
} // anonymous namespace
/// Describes ident structure that describes a source location.

View File

@ -241,6 +241,12 @@ enum class OpenMPOffloadMappingFlags : uint64_t {
LLVM_MARK_AS_BITMASK_ENUM(/* LargestFlag = */ OMP_MAP_MEMBER_OF)
};
enum OpenMPOffloadingReservedDeviceIDs {
/// Device ID if the device was not defined, runtime should get it
/// from environment variables in the spec.
OMP_DEVICEID_UNDEF = -1
};
enum class AddressSpace : unsigned {
Generic = 0,
Global = 1,

View File

@ -1097,6 +1097,9 @@ public:
/// variables.
StringMap<Constant*, BumpPtrAllocator> InternalVars;
/// Computes the size of type in bytes.
Value *getSizeInBytes(Value *BasePtr);
/// Create the global variable holding the offload mappings information.
GlobalVariable *createOffloadMaptypes(SmallVectorImpl<uint64_t> &Mappings,
std::string VarName);
@ -1552,6 +1555,29 @@ public:
StringRef EntryFnIDName,
int32_t NumTeams, int32_t NumThreads);
/// Generator for '#omp target data'
///
/// \param Loc The location where the target data construct was encountered.
/// \param CodeGenIP The insertion point at which the target directive code
/// should be placed.
/// \param MapTypeFlags BitVector storing the mapType flags for the
/// mapOperands.
/// \param MapNames Names for the mapOperands.
/// \param MapperAllocas Pointers to the AllocInsts for the map clause.
/// \param IsBegin If true then emits begin mapper call otherwise emits
/// end mapper call.
/// \param DeviceID Stores the DeviceID from the device clause.
/// \param IfCond Value which corresponds to the if clause condition.
/// \param ProcessMapOpCB Callback that generates code for the map clause.
/// \param BodyGenCB Callback that will generate the region code.
OpenMPIRBuilder::InsertPointTy createTargetData(
const LocationDescription &Loc, OpenMPIRBuilder::InsertPointTy CodeGenIP,
SmallVectorImpl<uint64_t> &MapTypeFlags,
SmallVectorImpl<Constant *> &MapNames,
struct MapperAllocas &MapperAllocas, bool IsBegin, int64_t DeviceID,
Value *IfCond, BodyGenCallbackTy ProcessMapOpCB,
BodyGenCallbackTy BodyGenCB = {});
/// Declarations for LLVM-IR types (simple, array, function and structure) are
/// generated below. Their names are defined and used in OpenMPKinds.def. Here
/// we provide the declarations, the initializeTypes function will provide the

View File

@ -4041,6 +4041,77 @@ Constant *OpenMPIRBuilder::registerTargetRegionFunction(
return OutlinedFnID;
}
OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::createTargetData(
const LocationDescription &Loc, OpenMPIRBuilder::InsertPointTy CodeGenIP,
SmallVectorImpl<uint64_t> &MapTypeFlags,
SmallVectorImpl<Constant *> &MapNames, struct MapperAllocas &MapperAllocas,
bool IsBegin, int64_t DeviceID, Value *IfCond,
BodyGenCallbackTy ProcessMapOpCB, BodyGenCallbackTy BodyGenCB) {
if (!updateToLocation(Loc))
return InsertPointTy();
Builder.restoreIP(CodeGenIP);
// LLVM utilities like blocks with terminators.
// The UI acts as a resume point for code insertion after the BodyGen
auto *UI = Builder.CreateUnreachable();
if (IfCond) {
auto *ThenTI =
SplitBlockAndInsertIfThen(IfCond, UI, /* Unreachable */ false);
ThenTI->getParent()->setName("omp_if.then");
Builder.SetInsertPoint(ThenTI);
} else {
Builder.SetInsertPoint(UI);
}
ProcessMapOpCB(Builder.saveIP(), Builder.saveIP());
uint32_t SrcLocStrSize;
Constant *SrcLocStr = getOrCreateSrcLocStr(Loc, SrcLocStrSize);
Value *srcLocInfo = getOrCreateIdent(SrcLocStr, SrcLocStrSize);
GlobalVariable *MapTypesGV =
createOffloadMaptypes(MapTypeFlags, ".offload_maptypes");
Value *MapTypesArg = Builder.CreateConstInBoundsGEP2_32(
ArrayType::get(Builder.getInt64Ty(), MapTypeFlags.size()), MapTypesGV,
/*Idx0=*/0, /*Idx1=*/0);
GlobalVariable *MapNamesGV =
createOffloadMapnames(MapNames, ".offload_mapnames");
Value *MapNamesArg = Builder.CreateConstInBoundsGEP2_32(
ArrayType::get(Builder.getInt8PtrTy(), MapNames.size()), MapNamesGV,
/*Idx0=*/0, /*Idx1=*/0);
Function *beginMapperFunc =
getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_begin_mapper);
Function *endMapperFunc =
getOrCreateRuntimeFunctionPtr(omp::OMPRTL___tgt_target_data_end_mapper);
if (BodyGenCB) {
// Create call to start the data region.
emitMapperCall(Builder.saveIP(), beginMapperFunc, srcLocInfo, MapTypesArg,
MapNamesArg, MapperAllocas, DeviceID, MapTypeFlags.size());
BodyGenCB(Builder.saveIP(), Builder.saveIP());
Builder.SetInsertPoint(UI->getParent());
// Create call to end the data region.
emitMapperCall(Builder.saveIP(), endMapperFunc, srcLocInfo, MapTypesArg,
MapNamesArg, MapperAllocas, DeviceID, MapTypeFlags.size());
} else {
emitMapperCall(Builder.saveIP(), IsBegin ? beginMapperFunc : endMapperFunc,
srcLocInfo, MapTypesArg, MapNamesArg, MapperAllocas,
DeviceID, MapTypeFlags.size());
}
// Update the insertion point and remove the terminator we introduced.
Builder.SetInsertPoint(UI->getParent());
if (IfCond)
UI->getParent()->setName("omp_if.end");
UI->eraseFromParent();
return Builder.saveIP();
}
std::string OpenMPIRBuilder::getNameWithSeparators(ArrayRef<StringRef> Parts,
StringRef FirstSeparator,
StringRef Separator) {
@ -4088,6 +4159,15 @@ Value *OpenMPIRBuilder::getOMPCriticalRegionLock(StringRef CriticalName) {
return getOrCreateInternalVariable(KmpCriticalNameTy, Name);
}
Value *OpenMPIRBuilder::getSizeInBytes(Value *BasePtr) {
LLVMContext &Ctx = Builder.getContext();
Value *Null = Constant::getNullValue(BasePtr->getType()->getPointerTo());
Value *SizeGep =
Builder.CreateGEP(BasePtr->getType(), Null, Builder.getInt32(1));
Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, Type::getInt64Ty(Ctx));
return SizePtrToInt;
}
GlobalVariable *
OpenMPIRBuilder::createOffloadMaptypes(SmallVectorImpl<uint64_t> &Mappings,
std::string VarName) {
@ -4111,9 +4191,12 @@ void OpenMPIRBuilder::createMapperAllocas(const LocationDescription &Loc,
auto *ArrI8PtrTy = ArrayType::get(Int8Ptr, NumOperands);
auto *ArrI64Ty = ArrayType::get(Int64, NumOperands);
Builder.restoreIP(AllocaIP);
AllocaInst *ArgsBase = Builder.CreateAlloca(ArrI8PtrTy);
AllocaInst *Args = Builder.CreateAlloca(ArrI8PtrTy);
AllocaInst *ArgSizes = Builder.CreateAlloca(ArrI64Ty);
AllocaInst *ArgsBase = Builder.CreateAlloca(
ArrI8PtrTy, /* ArraySize = */ nullptr, ".offload_baseptrs");
AllocaInst *Args = Builder.CreateAlloca(ArrI8PtrTy, /* ArraySize = */ nullptr,
".offload_ptrs");
AllocaInst *ArgSizes = Builder.CreateAlloca(
ArrI64Ty, /* ArraySize = */ nullptr, ".offload_sizes");
Builder.restoreIP(Loc.IP);
MapperAllocas.ArgsBase = ArgsBase;
MapperAllocas.Args = Args;

View File

@ -4912,6 +4912,245 @@ TEST_F(OpenMPIRBuilderTest, EmitMapperCall) {
EXPECT_TRUE(MapperCall->getOperand(8)->getType()->isPointerTy());
}
TEST_F(OpenMPIRBuilderTest, TargetEnterData) {
OpenMPIRBuilder OMPBuilder(*M);
OMPBuilder.initialize();
F->setName("func");
IRBuilder<> Builder(BB);
OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});
unsigned NumDataOperands = 1;
int64_t DeviceID = 2;
struct OpenMPIRBuilder::MapperAllocas MapperAllocas;
SmallVector<uint64_t> MapTypeFlagsTo = {1};
SmallVector<Constant *> MapNames;
auto *I8PtrTy = Builder.getInt8PtrTy();
auto *ArrI8PtrTy = ArrayType::get(I8PtrTy, NumDataOperands);
auto *I64Ty = Builder.getInt64Ty();
auto *ArrI64Ty = ArrayType::get(I64Ty, NumDataOperands);
AllocaInst *Val1 =
Builder.CreateAlloca(Builder.getInt32Ty(), Builder.getInt64(1));
ASSERT_NE(Val1, nullptr);
IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(),
F->getEntryBlock().getFirstInsertionPt());
OMPBuilder.createMapperAllocas(Builder.saveIP(), AllocaIP, NumDataOperands,
MapperAllocas);
using InsertPointTy = OpenMPIRBuilder::InsertPointTy;
auto ProcessMapOpCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {
Value *DataValue = Val1;
Value *DataPtrBase;
Value *DataPtr;
DataPtrBase = DataValue;
DataPtr = DataValue;
Builder.restoreIP(CodeGenIP);
Value *Null = Constant::getNullValue(DataValue->getType()->getPointerTo());
Value *SizeGep =
Builder.CreateGEP(DataValue->getType(), Null, Builder.getInt32(1));
Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, I64Ty);
Value *PtrBaseGEP =
Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,
{Builder.getInt32(0), Builder.getInt32(0)});
Value *PtrBaseCast = Builder.CreateBitCast(
PtrBaseGEP, DataPtrBase->getType()->getPointerTo());
Builder.CreateStore(DataPtrBase, PtrBaseCast);
Value *PtrGEP =
Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,
{Builder.getInt32(0), Builder.getInt32(0)});
Value *PtrCast =
Builder.CreateBitCast(PtrGEP, DataPtr->getType()->getPointerTo());
Builder.CreateStore(DataPtr, PtrCast);
Value *SizeGEP =
Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,
{Builder.getInt32(0), Builder.getInt32(0)});
Builder.CreateStore(SizePtrToInt, SizeGEP);
};
Builder.restoreIP(OMPBuilder.createTargetData(
Loc, Builder.saveIP(), MapTypeFlagsTo, MapNames, MapperAllocas,
/* IsBegin= */ true, DeviceID, /* IfCond= */ nullptr, ProcessMapOpCB));
CallInst *TargetDataCall = dyn_cast<CallInst>(&BB->back());
EXPECT_NE(TargetDataCall, nullptr);
EXPECT_EQ(TargetDataCall->arg_size(), 9U);
EXPECT_EQ(TargetDataCall->getCalledFunction()->getName(),
"__tgt_target_data_begin_mapper");
EXPECT_TRUE(TargetDataCall->getOperand(1)->getType()->isIntegerTy(64));
EXPECT_TRUE(TargetDataCall->getOperand(2)->getType()->isIntegerTy(32));
EXPECT_TRUE(TargetDataCall->getOperand(8)->getType()->isPointerTy());
Builder.CreateRetVoid();
EXPECT_FALSE(verifyModule(*M, &errs()));
}
TEST_F(OpenMPIRBuilderTest, TargetExitData) {
OpenMPIRBuilder OMPBuilder(*M);
OMPBuilder.initialize();
F->setName("func");
IRBuilder<> Builder(BB);
OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});
unsigned NumDataOperands = 1;
int64_t DeviceID = 2;
struct OpenMPIRBuilder::MapperAllocas MapperAllocas;
SmallVector<uint64_t> MapTypeFlagsFrom = {2};
SmallVector<Constant *> MapNames;
auto *I8PtrTy = Builder.getInt8PtrTy();
auto *ArrI8PtrTy = ArrayType::get(I8PtrTy, NumDataOperands);
auto *I64Ty = Builder.getInt64Ty();
auto *ArrI64Ty = ArrayType::get(I64Ty, NumDataOperands);
AllocaInst *Val1 =
Builder.CreateAlloca(Builder.getInt32Ty(), Builder.getInt64(1));
ASSERT_NE(Val1, nullptr);
IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(),
F->getEntryBlock().getFirstInsertionPt());
OMPBuilder.createMapperAllocas(Builder.saveIP(), AllocaIP, NumDataOperands,
MapperAllocas);
using InsertPointTy = OpenMPIRBuilder::InsertPointTy;
auto ProcessMapOpCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {
Value *DataValue = Val1;
Value *DataPtrBase;
Value *DataPtr;
DataPtrBase = DataValue;
DataPtr = DataValue;
Builder.restoreIP(CodeGenIP);
Value *Null = Constant::getNullValue(DataValue->getType()->getPointerTo());
Value *SizeGep =
Builder.CreateGEP(DataValue->getType(), Null, Builder.getInt32(1));
Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, I64Ty);
Value *PtrBaseGEP =
Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,
{Builder.getInt32(0), Builder.getInt32(0)});
Value *PtrBaseCast = Builder.CreateBitCast(
PtrBaseGEP, DataPtrBase->getType()->getPointerTo());
Builder.CreateStore(DataPtrBase, PtrBaseCast);
Value *PtrGEP =
Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,
{Builder.getInt32(0), Builder.getInt32(0)});
Value *PtrCast =
Builder.CreateBitCast(PtrGEP, DataPtr->getType()->getPointerTo());
Builder.CreateStore(DataPtr, PtrCast);
Value *SizeGEP =
Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,
{Builder.getInt32(0), Builder.getInt32(0)});
Builder.CreateStore(SizePtrToInt, SizeGEP);
};
Builder.restoreIP(OMPBuilder.createTargetData(
Loc, Builder.saveIP(), MapTypeFlagsFrom, MapNames, MapperAllocas,
/* IsBegin= */ false, DeviceID, /* IfCond= */ nullptr, ProcessMapOpCB));
CallInst *TargetDataCall = dyn_cast<CallInst>(&BB->back());
EXPECT_NE(TargetDataCall, nullptr);
EXPECT_EQ(TargetDataCall->arg_size(), 9U);
EXPECT_EQ(TargetDataCall->getCalledFunction()->getName(),
"__tgt_target_data_end_mapper");
EXPECT_TRUE(TargetDataCall->getOperand(1)->getType()->isIntegerTy(64));
EXPECT_TRUE(TargetDataCall->getOperand(2)->getType()->isIntegerTy(32));
EXPECT_TRUE(TargetDataCall->getOperand(8)->getType()->isPointerTy());
Builder.CreateRetVoid();
EXPECT_FALSE(verifyModule(*M, &errs()));
}
TEST_F(OpenMPIRBuilderTest, TargetDataRegion) {
OpenMPIRBuilder OMPBuilder(*M);
OMPBuilder.initialize();
F->setName("func");
IRBuilder<> Builder(BB);
OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL});
unsigned NumDataOperands = 1;
int64_t DeviceID = 2;
struct OpenMPIRBuilder::MapperAllocas MapperAllocas;
SmallVector<uint64_t> MapTypeFlagsToFrom = {3};
SmallVector<Constant *> MapNames;
auto *I8PtrTy = Builder.getInt8PtrTy();
auto *ArrI8PtrTy = ArrayType::get(I8PtrTy, NumDataOperands);
auto *I64Ty = Builder.getInt64Ty();
auto *ArrI64Ty = ArrayType::get(I64Ty, NumDataOperands);
AllocaInst *Val1 =
Builder.CreateAlloca(Builder.getInt32Ty(), Builder.getInt64(1));
ASSERT_NE(Val1, nullptr);
IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(),
F->getEntryBlock().getFirstInsertionPt());
OMPBuilder.createMapperAllocas(Builder.saveIP(), AllocaIP, NumDataOperands,
MapperAllocas);
using InsertPointTy = OpenMPIRBuilder::InsertPointTy;
auto ProcessMapOpCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP) {
Value *DataValue = Val1;
Value *DataPtrBase;
Value *DataPtr;
DataPtrBase = DataValue;
DataPtr = DataValue;
Builder.restoreIP(CodeGenIP);
Value *Null = Constant::getNullValue(DataValue->getType()->getPointerTo());
Value *SizeGep =
Builder.CreateGEP(DataValue->getType(), Null, Builder.getInt32(1));
Value *SizePtrToInt = Builder.CreatePtrToInt(SizeGep, I64Ty);
Value *PtrBaseGEP =
Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.ArgsBase,
{Builder.getInt32(0), Builder.getInt32(0)});
Value *PtrBaseCast = Builder.CreateBitCast(
PtrBaseGEP, DataPtrBase->getType()->getPointerTo());
Builder.CreateStore(DataPtrBase, PtrBaseCast);
Value *PtrGEP =
Builder.CreateInBoundsGEP(ArrI8PtrTy, MapperAllocas.Args,
{Builder.getInt32(0), Builder.getInt32(0)});
Value *PtrCast =
Builder.CreateBitCast(PtrGEP, DataPtr->getType()->getPointerTo());
Builder.CreateStore(DataPtr, PtrCast);
Value *SizeGEP =
Builder.CreateInBoundsGEP(ArrI64Ty, MapperAllocas.ArgSizes,
{Builder.getInt32(0), Builder.getInt32(0)});
Builder.CreateStore(SizePtrToInt, SizeGEP);
};
auto BodyCB = [&](InsertPointTy allocaIP, InsertPointTy codeGenIP) {
Builder.restoreIP(codeGenIP);
auto *SI = Builder.CreateStore(Builder.getInt32(99), Val1);
auto *newBB = SplitBlock(Builder.GetInsertBlock(), SI);
Builder.SetInsertPoint(newBB);
auto *UI = &Builder.GetInsertBlock()->back();
SplitBlock(Builder.GetInsertBlock(), UI);
};
Builder.restoreIP(OMPBuilder.createTargetData(
Loc, Builder.saveIP(), MapTypeFlagsToFrom, MapNames, MapperAllocas,
/* IsBegin= */ false, DeviceID, /* IfCond= */ nullptr, ProcessMapOpCB,
BodyCB));
CallInst *TargetDataCall =
dyn_cast<CallInst>(&Builder.GetInsertBlock()->back());
EXPECT_NE(TargetDataCall, nullptr);
EXPECT_EQ(TargetDataCall->arg_size(), 9U);
EXPECT_EQ(TargetDataCall->getCalledFunction()->getName(),
"__tgt_target_data_end_mapper");
EXPECT_TRUE(TargetDataCall->getOperand(1)->getType()->isIntegerTy(64));
EXPECT_TRUE(TargetDataCall->getOperand(2)->getType()->isIntegerTy(32));
EXPECT_TRUE(TargetDataCall->getOperand(8)->getType()->isPointerTy());
Builder.CreateRetVoid();
EXPECT_FALSE(verifyModule(*M, &errs()));
}
TEST_F(OpenMPIRBuilderTest, CreateTask) {
using InsertPointTy = OpenMPIRBuilder::InsertPointTy;
OpenMPIRBuilder OMPBuilder(*M);

View File

@ -0,0 +1,40 @@
//===- OpenMPCommon.h - Utils for translating MLIR dialect to LLVM IR------===//
//
// 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 defines general utilities for MLIR Dialect translations to LLVM IR.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_TARGET_LLVMIR_DIALECT_OPENMPCOMMON_H
#define MLIR_TARGET_LLVMIR_DIALECT_OPENMPCOMMON_H
#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/IR/Location.h"
#include "mlir/Support/LLVM.h"
#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
#include "llvm/IR/IRBuilder.h"
namespace mlir {
namespace LLVM {
/// Create a constant string location from the MLIR Location information.
llvm::Constant *createSourceLocStrFromLocation(Location loc,
llvm::OpenMPIRBuilder &builder,
StringRef name,
uint32_t &strLen);
/// Create a constant string representing the mapping information extracted from
/// the MLIR location information.
llvm::Constant *createMappingInformation(Location loc,
llvm::OpenMPIRBuilder &builder);
} // namespace LLVM
} // namespace mlir
#endif // MLIR_TARGET_LLVMIR_DIALECT_OPENMPCOMMON_H

View File

@ -19,6 +19,7 @@ add_mlir_translation_library(MLIRTargetLLVMIRExport
LoopAnnotationTranslation.cpp
ModuleTranslation.cpp
TypeToLLVM.cpp
Dialect/OpenMPCommon.cpp
ADDITIONAL_HEADER_DIRS
${MLIR_MAIN_INCLUDE_DIR}/mlir/Target/LLVMIR

View File

@ -18,6 +18,7 @@
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Operation.h"
#include "mlir/Support/LLVM.h"
#include "mlir/Target/LLVMIR/Dialect/OpenMPCommon.h"
#include "mlir/Target/LLVMIR/ModuleTranslation.h"
#include "llvm/ADT/TypeSwitch.h"
@ -46,23 +47,6 @@ 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,
uint32_t &strLen) {
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, strLen);
}
std::string locStr;
llvm::raw_string_ostream locOS(locStr);
locOS << loc;
return builder.getOrCreateSrcLocStr(locOS.str(), strLen);
}
/// Create the location struct from the operation location information.
static llvm::Value *createSourceLocationInfo(OpenACCIRBuilder &builder,
Operation *op) {
@ -70,24 +54,11 @@ static llvm::Value *createSourceLocationInfo(OpenACCIRBuilder &builder,
auto funcOp = op->getParentOfType<LLVM::LLVMFuncOp>();
StringRef funcName = funcOp ? funcOp.getName() : "unknown";
uint32_t strLen;
llvm::Constant *locStr =
createSourceLocStrFromLocation(loc, builder, funcName, strLen);
llvm::Constant *locStr = mlir::LLVM::createSourceLocStrFromLocation(
loc, builder, funcName, strLen);
return builder.getOrCreateIdent(locStr, strLen);
}
/// Create a constant string representing the mapping information extracted from
/// the MLIR location information.
static llvm::Constant *createMappingInformation(Location loc,
OpenACCIRBuilder &builder) {
uint32_t strLen;
if (auto nameLoc = loc.dyn_cast<NameLoc>()) {
StringRef name = nameLoc.getName();
return createSourceLocStrFromLocation(nameLoc.getChildLoc(), builder, name,
strLen);
}
return createSourceLocStrFromLocation(loc, builder, "unknown", strLen);
}
/// Return the runtime function used to lower the given operation.
static llvm::Function *getAssociatedFunction(OpenACCIRBuilder &builder,
Operation *op) {
@ -107,19 +78,6 @@ static llvm::Function *getAssociatedFunction(OpenACCIRBuilder &builder,
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
@ -153,7 +111,7 @@ processOperands(llvm::IRBuilderBase &builder,
} else if (data.getType().isa<LLVM::LLVMPointerType>()) {
dataPtrBase = dataValue;
dataPtr = dataValue;
dataSize = getSizeInBytes(builder, dataValue);
dataSize = accBuilder->getSizeInBytes(dataValue);
} else {
return op->emitOpError()
<< "Data operand must be legalized before translation."
@ -185,7 +143,7 @@ processOperands(llvm::IRBuilderBase &builder,
flags.push_back(operandFlag);
llvm::Constant *mapName =
createMappingInformation(data.getLoc(), *accBuilder);
mlir::LLVM::createMappingInformation(data.getLoc(), *accBuilder);
names.push_back(mapName);
++index;
}

View File

@ -15,10 +15,12 @@
#include "mlir/IR/IRMapping.h"
#include "mlir/IR/Operation.h"
#include "mlir/Support/LLVM.h"
#include "mlir/Target/LLVMIR/Dialect/OpenMPCommon.h"
#include "mlir/Target/LLVMIR/ModuleTranslation.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/Frontend/OpenMP/OMPConstants.h"
#include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/IRBuilder.h"
@ -1351,6 +1353,195 @@ convertOmpThreadprivate(Operation &opInst, llvm::IRBuilderBase &builder,
return success();
}
/// Process MapOperands for Target Data directives.
static LogicalResult processMapOperand(
llvm::IRBuilderBase &builder, LLVM::ModuleTranslation &moduleTranslation,
const SmallVector<Value> &mapOperands, const ArrayAttr &mapTypes,
SmallVector<uint64_t> &mapTypeFlags,
SmallVectorImpl<llvm::Constant *> &mapNames,
struct llvm::OpenMPIRBuilder::MapperAllocas &mapperAllocas) {
auto numMapOperands = mapOperands.size();
llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();
llvm::PointerType *i8PtrTy = builder.getInt8PtrTy();
llvm::ArrayType *arrI8PtrTy = llvm::ArrayType::get(i8PtrTy, numMapOperands);
llvm::IntegerType *i64Ty = builder.getInt64Ty();
llvm::ArrayType *arrI64Ty = llvm::ArrayType::get(i64Ty, numMapOperands);
unsigned index = 0;
for (const auto &mapOp : mapOperands) {
const auto &mapTypeOp = mapTypes[index];
llvm::Value *mapOpValue = moduleTranslation.lookupValue(mapOp);
llvm::Value *mapOpPtrBase;
llvm::Value *mapOpPtr;
llvm::Value *mapOpSize;
if (mapOp.getType().isa<LLVM::LLVMPointerType>()) {
mapOpPtrBase = mapOpValue;
mapOpPtr = mapOpValue;
mapOpSize = ompBuilder->getSizeInBytes(mapOpValue);
} else {
return failure();
}
// 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, mapOpPtrBase->getType()->getPointerTo());
builder.CreateStore(mapOpPtrBase, 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, mapOpPtr->getType()->getPointerTo());
builder.CreateStore(mapOpPtr, 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(mapOpSize, sizeGEP);
mapTypeFlags.push_back(mapTypeOp.dyn_cast<mlir::IntegerAttr>().getInt());
llvm::Constant *mapName =
mlir::LLVM::createMappingInformation(mapOp.getLoc(), *ompBuilder);
mapNames.push_back(mapName);
++index;
}
return success();
}
static LogicalResult
convertOmpTargetData(Operation *op, llvm::IRBuilderBase &builder,
LLVM::ModuleTranslation &moduleTranslation) {
unsigned numMapOperands;
bool mapperFunc;
llvm::Value *ifCond = nullptr;
int64_t deviceID = llvm::omp::OMP_DEVICEID_UNDEF;
SmallVector<Value> mapOperands;
ArrayAttr mapTypes;
llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();
LogicalResult result =
llvm::TypeSwitch<Operation *, LogicalResult>(op)
.Case([&](omp::DataOp dataOp) {
if (dataOp.getUseDeviceAddr().size() ||
dataOp.getUseDevicePtr().size())
return failure();
if (auto ifExprVar = dataOp.getIfExpr())
ifCond = moduleTranslation.lookupValue(ifExprVar);
if (auto devId = dataOp.getDevice())
if (auto constOp = mlir::dyn_cast<mlir::LLVM::ConstantOp>(
devId.getDefiningOp()))
if (auto intAttr =
constOp.getValue().dyn_cast<mlir::IntegerAttr>())
deviceID = intAttr.getInt();
numMapOperands = dataOp.getMapOperands().size();
mapOperands = dataOp.getMapOperands();
mapTypes = dataOp.getMapTypes();
return success();
})
.Case([&](omp::EnterDataOp enterDataOp) {
if (enterDataOp.getNowait())
return failure();
if (auto ifExprVar = enterDataOp.getIfExpr())
ifCond = moduleTranslation.lookupValue(ifExprVar);
if (auto devId = enterDataOp.getDevice())
if (auto constOp = mlir::dyn_cast<mlir::LLVM::ConstantOp>(
devId.getDefiningOp()))
if (auto intAttr =
constOp.getValue().dyn_cast<mlir::IntegerAttr>())
deviceID = intAttr.getInt();
numMapOperands = enterDataOp.getMapOperands().size();
mapOperands = enterDataOp.getMapOperands();
mapTypes = enterDataOp.getMapTypes();
mapperFunc = true;
return success();
})
.Case([&](omp::ExitDataOp exitDataOp) {
if (exitDataOp.getNowait())
return failure();
if (auto ifExprVar = exitDataOp.getIfExpr())
ifCond = moduleTranslation.lookupValue(ifExprVar);
if (auto devId = exitDataOp.getDevice())
if (auto constOp = mlir::dyn_cast<mlir::LLVM::ConstantOp>(
devId.getDefiningOp()))
if (auto intAttr =
constOp.getValue().dyn_cast<mlir::IntegerAttr>())
deviceID = intAttr.getInt();
numMapOperands = exitDataOp.getMapOperands().size();
mapOperands = exitDataOp.getMapOperands();
mapTypes = exitDataOp.getMapTypes();
mapperFunc = false;
return success();
})
.Default([&](Operation *op) {
return op->emitError("unsupported OpenMP operation: ")
<< op->getName();
});
if (failed(result))
return failure();
llvm::OpenMPIRBuilder::LocationDescription ompLoc(builder);
llvm::OpenMPIRBuilder::InsertPointTy allocaIP =
findAllocaInsertPoint(builder, moduleTranslation);
struct llvm::OpenMPIRBuilder::MapperAllocas mapperAllocas;
SmallVector<uint64_t> mapTypeFlags;
SmallVector<llvm::Constant *> mapNames;
ompBuilder->createMapperAllocas(builder.saveIP(), allocaIP, numMapOperands,
mapperAllocas);
using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;
LogicalResult processMapOpStatus = success();
auto processMapOpCB = [&](InsertPointTy allocaIP, InsertPointTy codeGenIP) {
builder.restoreIP(codeGenIP);
processMapOpStatus =
processMapOperand(builder, moduleTranslation, mapOperands, mapTypes,
mapTypeFlags, mapNames, mapperAllocas);
};
LogicalResult bodyGenStatus = success();
auto bodyCB = [&](InsertPointTy allocaIP, InsertPointTy codeGenIP) {
// DataOp has only one region associated with it.
auto &region = cast<omp::DataOp>(op).getRegion();
builder.restoreIP(codeGenIP);
convertOmpOpRegions(region, "omp.data.region", builder, moduleTranslation,
bodyGenStatus);
};
if (isa<omp::DataOp>(op)) {
builder.restoreIP(ompBuilder->createTargetData(
ompLoc, builder.saveIP(), mapTypeFlags, mapNames, mapperAllocas,
mapperFunc, deviceID, ifCond, processMapOpCB, bodyCB));
} else {
builder.restoreIP(ompBuilder->createTargetData(
ompLoc, builder.saveIP(), mapTypeFlags, mapNames, mapperAllocas,
mapperFunc, deviceID, ifCond, processMapOpCB));
}
if (failed(processMapOpStatus))
return processMapOpStatus;
return bodyGenStatus;
}
namespace {
/// Implementation of the dialect interface that converts operations belonging
@ -1465,6 +1656,9 @@ LogicalResult OpenMPDialectLLVMIRTranslationInterface::convertOperation(
.Case([&](omp::ThreadprivateOp) {
return convertOmpThreadprivate(*op, builder, moduleTranslation);
})
.Case<omp::DataOp, omp::EnterDataOp, omp::ExitDataOp>([&](auto op) {
return convertOmpTargetData(op, builder, moduleTranslation);
})
.Default([&](Operation *inst) {
return inst->emitError("unsupported OpenMP operation: ")
<< inst->getName();

View File

@ -0,0 +1,41 @@
//===- OpenMPCommon.cpp - Utils for translating MLIR dialect to LLVM IR----===//
//
// 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 defines general utilities for MLIR Dialect translations to LLVM IR.
//
//===----------------------------------------------------------------------===//
#include "mlir/Target/LLVMIR/Dialect/OpenMPCommon.h"
llvm::Constant *
mlir::LLVM::createSourceLocStrFromLocation(Location loc,
llvm::OpenMPIRBuilder &builder,
StringRef name, uint32_t &strLen) {
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, strLen);
}
std::string locStr;
llvm::raw_string_ostream locOS(locStr);
locOS << loc;
return builder.getOrCreateSrcLocStr(locOS.str(), strLen);
}
llvm::Constant *
mlir::LLVM::createMappingInformation(Location loc,
llvm::OpenMPIRBuilder &builder) {
uint32_t strLen;
if (auto nameLoc = loc.dyn_cast<NameLoc>()) {
StringRef name = nameLoc.getName();
return createSourceLocStrFromLocation(nameLoc.getChildLoc(), builder, name,
strLen);
}
return createSourceLocStrFromLocation(loc, builder, "unknown", strLen);
}

View File

@ -0,0 +1,176 @@
// RUN: mlir-translate -mlir-to-llvmir -split-input-file %s | FileCheck %s
llvm.func @_QPopenmp_target_data() {
%0 = llvm.mlir.constant(1 : i64) : i64
%1 = llvm.alloca %0 x i32 {bindc_name = "i", in_type = i32, operand_segment_sizes = array<i32: 0, 0>, uniq_name = "_QFopenmp_target_dataEi"} : (i64) -> !llvm.ptr<i32>
omp.target_data map((tofrom -> %1 : !llvm.ptr<i32>)) {
%2 = llvm.mlir.constant(99 : i32) : i32
llvm.store %2, %1 : !llvm.ptr<i32>
omp.terminator
}
llvm.return
}
// CHECK: @.offload_maptypes = private unnamed_addr constant [1 x i64] [i64 3]
// CHECK-LABEL: define void @_QPopenmp_target_data() {
// CHECK: %[[VAL_0:.*]] = alloca [1 x ptr], align 8
// CHECK: %[[VAL_1:.*]] = alloca [1 x ptr], align 8
// CHECK: %[[VAL_2:.*]] = alloca [1 x i64], align 8
// CHECK: %[[VAL_3:.*]] = alloca i32, i64 1, align 4
// CHECK: br label %[[VAL_4:.*]]
// CHECK: entry: ; preds = %[[VAL_5:.*]]
// CHECK: %[[VAL_6:.*]] = getelementptr inbounds [1 x ptr], ptr %[[VAL_0]], i32 0, i32 0
// CHECK: store ptr %[[VAL_3]], ptr %[[VAL_6]], align 8
// CHECK: %[[VAL_7:.*]] = getelementptr inbounds [1 x ptr], ptr %[[VAL_1]], i32 0, i32 0
// CHECK: store ptr %[[VAL_3]], ptr %[[VAL_7]], align 8
// CHECK: %[[VAL_8:.*]] = getelementptr inbounds [1 x i64], ptr %[[VAL_2]], i32 0, i32 0
// CHECK: store i64 ptrtoint (ptr getelementptr (ptr, ptr null, i32 1) to i64), ptr %[[VAL_8]], align 4
// CHECK: %[[VAL_9:.*]] = getelementptr inbounds [1 x ptr], ptr %[[VAL_0]], i32 0, i32 0
// CHECK: %[[VAL_10:.*]] = getelementptr inbounds [1 x ptr], ptr %[[VAL_1]], i32 0, i32 0
// CHECK: %[[VAL_11:.*]] = getelementptr inbounds [1 x i64], ptr %[[VAL_2]], i32 0, i32 0
// CHECK: call void @__tgt_target_data_begin_mapper(ptr @2, i64 -1, i32 1, ptr %[[VAL_9]], ptr %[[VAL_10]], ptr %[[VAL_11]], ptr @.offload_maptypes, ptr @.offload_mapnames, ptr null)
// CHECK: br label %[[VAL_12:.*]]
// CHECK: omp.data.region: ; preds = %[[VAL_4]]
// CHECK: store i32 99, ptr %[[VAL_3]], align 4
// CHECK: br label %[[VAL_13:.*]]
// CHECK: omp.region.cont: ; preds = %[[VAL_12]]
// CHECK: %[[VAL_14:.*]] = getelementptr inbounds [1 x ptr], ptr %[[VAL_0]], i32 0, i32 0
// CHECK: %[[VAL_15:.*]] = getelementptr inbounds [1 x ptr], ptr %[[VAL_1]], i32 0, i32 0
// CHECK: %[[VAL_16:.*]] = getelementptr inbounds [1 x i64], ptr %[[VAL_2]], i32 0, i32 0
// CHECK: call void @__tgt_target_data_end_mapper(ptr @2, i64 -1, i32 1, ptr %[[VAL_14]], ptr %[[VAL_15]], ptr %[[VAL_16]], ptr @.offload_maptypes, ptr @.offload_mapnames, ptr null)
// CHECK: ret void
// -----
llvm.func @_QPopenmp_target_data_region(%1 : !llvm.ptr<array<1024 x i32>>) {
omp.target_data map((from -> %1 : !llvm.ptr<array<1024 x i32>>)) {
%2 = llvm.mlir.constant(99 : i32) : i32
%3 = llvm.mlir.constant(1 : i64) : i64
%4 = llvm.mlir.constant(1 : i64) : i64
%5 = llvm.mlir.constant(0 : i64) : i64
%6 = llvm.getelementptr %1[0, %5] : (!llvm.ptr<array<1024 x i32>>, i64) -> !llvm.ptr<i32>
llvm.store %2, %6 : !llvm.ptr<i32>
omp.terminator
}
llvm.return
}
// CHECK: @.offload_maptypes = private unnamed_addr constant [1 x i64] [i64 2]
// CHECK-LABEL: define void @_QPopenmp_target_data_region
// CHECK: (ptr %[[ARG_0:.*]]) {
// CHECK: %[[VAL_0:.*]] = alloca [1 x ptr], align 8
// CHECK: %[[VAL_1:.*]] = alloca [1 x ptr], align 8
// CHECK: %[[VAL_2:.*]] = alloca [1 x i64], align 8
// CHECK: br label %[[VAL_3:.*]]
// CHECK: entry: ; preds = %[[VAL_4:.*]]
// CHECK: %[[VAL_5:.*]] = getelementptr inbounds [1 x ptr], ptr %[[VAL_0]], i32 0, i32 0
// CHECK: store ptr %[[VAL_6:.*]], ptr %[[VAL_5]], align 8
// CHECK: %[[VAL_7:.*]] = getelementptr inbounds [1 x ptr], ptr %[[VAL_1]], i32 0, i32 0
// CHECK: store ptr %[[VAL_6]], ptr %[[VAL_7]], align 8
// CHECK: %[[VAL_8:.*]] = getelementptr inbounds [1 x i64], ptr %[[VAL_2]], i32 0, i32 0
// CHECK: store i64 ptrtoint (ptr getelementptr (ptr, ptr null, i32 1) to i64), ptr %[[VAL_8]], align 4
// CHECK: %[[VAL_9:.*]] = getelementptr inbounds [1 x ptr], ptr %[[VAL_0]], i32 0, i32 0
// CHECK: %[[VAL_10:.*]] = getelementptr inbounds [1 x ptr], ptr %[[VAL_1]], i32 0, i32 0
// CHECK: %[[VAL_11:.*]] = getelementptr inbounds [1 x i64], ptr %[[VAL_2]], i32 0, i32 0
// CHECK: call void @__tgt_target_data_begin_mapper(ptr @2, i64 -1, i32 1, ptr %[[VAL_9]], ptr %[[VAL_10]], ptr %[[VAL_11]], ptr @.offload_maptypes, ptr @.offload_mapnames, ptr null)
// CHECK: br label %[[VAL_12:.*]]
// CHECK: omp.data.region: ; preds = %[[VAL_3]]
// CHECK: %[[VAL_13:.*]] = getelementptr [1024 x i32], ptr %[[VAL_6]], i32 0, i64 0
// CHECK: store i32 99, ptr %[[VAL_13]], align 4
// CHECK: br label %[[VAL_14:.*]]
// CHECK: omp.region.cont: ; preds = %[[VAL_12]]
// CHECK: %[[VAL_15:.*]] = getelementptr inbounds [1 x ptr], ptr %[[VAL_0]], i32 0, i32 0
// CHECK: %[[VAL_16:.*]] = getelementptr inbounds [1 x ptr], ptr %[[VAL_1]], i32 0, i32 0
// CHECK: %[[VAL_17:.*]] = getelementptr inbounds [1 x i64], ptr %[[VAL_2]], i32 0, i32 0
// CHECK: call void @__tgt_target_data_end_mapper(ptr @2, i64 -1, i32 1, ptr %[[VAL_15]], ptr %[[VAL_16]], ptr %[[VAL_17]], ptr @.offload_maptypes, ptr @.offload_mapnames, ptr null)
// CHECK: ret void
// -----
llvm.func @_QPomp_target_enter_exit(%1 : !llvm.ptr<array<1024 x i32>>, %3 : !llvm.ptr<array<1024 x i32>>) {
%4 = llvm.mlir.constant(1 : i64) : i64
%5 = llvm.alloca %4 x i32 {bindc_name = "dvc", in_type = i32, operand_segment_sizes = array<i32: 0, 0>, uniq_name = "_QFomp_target_enter_exitEdvc"} : (i64) -> !llvm.ptr<i32>
%6 = llvm.mlir.constant(1 : i64) : i64
%7 = llvm.alloca %6 x i32 {bindc_name = "i", in_type = i32, operand_segment_sizes = array<i32: 0, 0>, uniq_name = "_QFomp_target_enter_exitEi"} : (i64) -> !llvm.ptr<i32>
%8 = llvm.mlir.constant(5 : i32) : i32
llvm.store %8, %7 : !llvm.ptr<i32>
%9 = llvm.mlir.constant(2 : i32) : i32
llvm.store %9, %5 : !llvm.ptr<i32>
%10 = llvm.load %7 : !llvm.ptr<i32>
%11 = llvm.mlir.constant(10 : i32) : i32
%12 = llvm.icmp "slt" %10, %11 : i32
%13 = llvm.load %5 : !llvm.ptr<i32>
omp.target_enter_data if(%12 : i1) device(%13 : i32) map((to -> %1 : !llvm.ptr<array<1024 x i32>>), (alloc -> %3 : !llvm.ptr<array<1024 x i32>>))
%14 = llvm.load %7 : !llvm.ptr<i32>
%15 = llvm.mlir.constant(10 : i32) : i32
%16 = llvm.icmp "sgt" %14, %15 : i32
%17 = llvm.load %5 : !llvm.ptr<i32>
omp.target_exit_data if(%16 : i1) device(%17 : i32) map((from -> %1 : !llvm.ptr<array<1024 x i32>>), (release -> %3 : !llvm.ptr<array<1024 x i32>>))
llvm.return
}
// CHECK: @.offload_maptypes = private unnamed_addr constant [2 x i64] [i64 1, i64 0]
// CHECK: @.offload_maptypes.1 = private unnamed_addr constant [2 x i64] [i64 2, i64 0]
// CHECK-LABEL: define void @_QPomp_target_enter_exit
// CHECK: (ptr %[[ARG_0:.*]], ptr %[[ARG_1:.*]]) {
// CHECK: %[[VAL_0:.*]] = alloca [2 x ptr], align 8
// CHECK: %[[VAL_1:.*]] = alloca [2 x ptr], align 8
// CHECK: %[[VAL_2:.*]] = alloca [2 x i64], align 8
// CHECK: %[[VAL_3:.*]] = alloca [2 x ptr], align 8
// CHECK: %[[VAL_4:.*]] = alloca [2 x ptr], align 8
// CHECK: %[[VAL_5:.*]] = alloca [2 x i64], align 8
// CHECK: %[[VAL_6:.*]] = alloca i32, i64 1, align 4
// CHECK: %[[VAL_7:.*]] = alloca i32, i64 1, align 4
// CHECK: store i32 5, ptr %[[VAL_7]], align 4
// CHECK: store i32 2, ptr %[[VAL_6]], align 4
// CHECK: %[[VAL_8:.*]] = load i32, ptr %[[VAL_7]], align 4
// CHECK: %[[VAL_9:.*]] = icmp slt i32 %[[VAL_8]], 10
// CHECK: %[[VAL_10:.*]] = load i32, ptr %[[VAL_6]], align 4
// CHECK: br label %[[VAL_11:.*]]
// CHECK: entry: ; preds = %[[VAL_12:.*]]
// CHECK: br i1 %[[VAL_9]], label %[[VAL_13:.*]], label %[[VAL_14:.*]]
// CHECK: omp_if.then: ; preds = %[[VAL_11]]
// CHECK: %[[VAL_15:.*]] = getelementptr inbounds [2 x ptr], ptr %[[VAL_3]], i32 0, i32 0
// CHECK: store ptr %[[VAL_16:.*]], ptr %[[VAL_15]], align 8
// CHECK: %[[VAL_17:.*]] = getelementptr inbounds [2 x ptr], ptr %[[VAL_4]], i32 0, i32 0
// CHECK: store ptr %[[VAL_16]], ptr %[[VAL_17]], align 8
// CHECK: %[[VAL_18:.*]] = getelementptr inbounds [2 x i64], ptr %[[VAL_5]], i32 0, i32 0
// CHECK: store i64 ptrtoint (ptr getelementptr (ptr, ptr null, i32 1) to i64), ptr %[[VAL_18]], align 4
// CHECK: %[[VAL_19:.*]] = getelementptr inbounds [2 x ptr], ptr %[[VAL_3]], i32 0, i32 1
// CHECK: store ptr %[[VAL_20:.*]], ptr %[[VAL_19]], align 8
// CHECK: %[[VAL_21:.*]] = getelementptr inbounds [2 x ptr], ptr %[[VAL_4]], i32 0, i32 1
// CHECK: store ptr %[[VAL_20]], ptr %[[VAL_21]], align 8
// CHECK: %[[VAL_22:.*]] = getelementptr inbounds [2 x i64], ptr %[[VAL_5]], i32 0, i32 1
// CHECK: store i64 ptrtoint (ptr getelementptr (ptr, ptr null, i32 1) to i64), ptr %[[VAL_22]], align 4
// CHECK: %[[VAL_23:.*]] = getelementptr inbounds [2 x ptr], ptr %[[VAL_3]], i32 0, i32 0
// CHECK: %[[VAL_24:.*]] = getelementptr inbounds [2 x ptr], ptr %[[VAL_4]], i32 0, i32 0
// CHECK: %[[VAL_25:.*]] = getelementptr inbounds [2 x i64], ptr %[[VAL_5]], i32 0, i32 0
// CHECK: call void @__tgt_target_data_begin_mapper(ptr @3, i64 -1, i32 2, ptr %[[VAL_23]], ptr %[[VAL_24]], ptr %[[VAL_25]], ptr @.offload_maptypes, ptr @.offload_mapnames, ptr null)
// CHECK: br label %[[VAL_14]]
// CHECK: omp_if.end: ; preds = %[[VAL_11]], %[[VAL_13]]
// CHECK: %[[VAL_26:.*]] = load i32, ptr %[[VAL_7]], align 4
// CHECK: %[[VAL_27:.*]] = icmp sgt i32 %[[VAL_26]], 10
// CHECK: %[[VAL_28:.*]] = load i32, ptr %[[VAL_6]], align 4
// CHECK: br i1 %[[VAL_27]], label %[[VAL_29:.*]], label %[[VAL_30:.*]]
// CHECK: omp_if.then4: ; preds = %[[VAL_14]]
// CHECK: %[[VAL_31:.*]] = getelementptr inbounds [2 x ptr], ptr %[[VAL_0]], i32 0, i32 0
// CHECK: store ptr %[[VAL_16]], ptr %[[VAL_31]], align 8
// CHECK: %[[VAL_32:.*]] = getelementptr inbounds [2 x ptr], ptr %[[VAL_1]], i32 0, i32 0
// CHECK: store ptr %[[VAL_16]], ptr %[[VAL_32]], align 8
// CHECK: %[[VAL_33:.*]] = getelementptr inbounds [2 x i64], ptr %[[VAL_2]], i32 0, i32 0
// CHECK: store i64 ptrtoint (ptr getelementptr (ptr, ptr null, i32 1) to i64), ptr %[[VAL_33]], align 4
// CHECK: %[[VAL_34:.*]] = getelementptr inbounds [2 x ptr], ptr %[[VAL_0]], i32 0, i32 1
// CHECK: store ptr %[[VAL_20]], ptr %[[VAL_34]], align 8
// CHECK: %[[VAL_35:.*]] = getelementptr inbounds [2 x ptr], ptr %[[VAL_1]], i32 0, i32 1
// CHECK: store ptr %[[VAL_20]], ptr %[[VAL_35]], align 8
// CHECK: %[[VAL_36:.*]] = getelementptr inbounds [2 x i64], ptr %[[VAL_2]], i32 0, i32 1
// CHECK: store i64 ptrtoint (ptr getelementptr (ptr, ptr null, i32 1) to i64), ptr %[[VAL_36]], align 4
// CHECK: %[[VAL_37:.*]] = getelementptr inbounds [2 x ptr], ptr %[[VAL_0]], i32 0, i32 0
// CHECK: %[[VAL_38:.*]] = getelementptr inbounds [2 x ptr], ptr %[[VAL_1]], i32 0, i32 0
// CHECK: %[[VAL_39:.*]] = getelementptr inbounds [2 x i64], ptr %[[VAL_2]], i32 0, i32 0
// CHECK: call void @__tgt_target_data_end_mapper(ptr @3, i64 -1, i32 2, ptr %[[VAL_37]], ptr %[[VAL_38]], ptr %[[VAL_39]], ptr @.offload_maptypes.1, ptr @.offload_mapnames.2, ptr null)
// CHECK: br label %[[VAL_30]]
// CHECK: omp_if.end5: ; preds = %[[VAL_14]], %[[VAL_29]]
// CHECK: ret void
// -----