[mlir][openacc] Add OpenACC translation to LLVM IR (enter_data op create/copyin)

This patch begins to translate acc.enter_data operation to call to tgt runtime call.
It currently only translate create/copyin operands of memref type. This acts as a basis to add support
for FIR types in the Flang/OpenACC support. It follows more or less a similar path than clang
with `omp target enter data map` directives.
This patch is taking a different approach than D100678 and perform a translation to LLVM IR
and make use of the OpenMPIRBuilder instead of doing a conversion to the LLVMIR dialect.

OpenACC support in Flang will rely on the current OpenMP runtime where 1:1 lowering can be
applied. Some extension will be added where features are not available yet.

Big part of this code will be shared for other standalone data operations in the OpenACC
dialect such as acc.exit_data and acc.update.

It is likely that parts of the lowering can also be shared later with the ops for
standalone data directives in the OpenMP dialect when they are introduced.

This is an initial translation and it probably needs more work.

Reviewed By: ftynse

Differential Revision: https://reviews.llvm.org/D101504
This commit is contained in:
Valentin Clement 2021-05-12 11:41:03 -04:00 committed by clementval
parent 81f56a2eb3
commit 113b807017
7 changed files with 419 additions and 0 deletions

View File

@ -19,6 +19,7 @@
#include "mlir/Target/LLVMIR/Dialect/ArmSVE/ArmSVEToLLVMIRTranslation.h"
#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
#include "mlir/Target/LLVMIR/Dialect/NVVM/NVVMToLLVMIRTranslation.h"
#include "mlir/Target/LLVMIR/Dialect/OpenACC/OpenACCToLLVMIRTranslation.h"
#include "mlir/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.h"
#include "mlir/Target/LLVMIR/Dialect/ROCDL/ROCDLToLLVMIRTranslation.h"
#include "mlir/Target/LLVMIR/Dialect/X86Vector/X86VectorToLLVMIRTranslation.h"
@ -34,6 +35,7 @@ static inline void registerAllToLLVMIRTranslations(DialectRegistry &registry) {
registerArmSVEDialectTranslation(registry);
registerLLVMDialectTranslation(registry);
registerNVVMDialectTranslation(registry);
registerOpenACCDialectTranslation(registry);
registerOpenMPDialectTranslation(registry);
registerROCDLDialectTranslation(registry);
registerX86VectorDialectTranslation(registry);

View File

@ -0,0 +1,31 @@
//===- OpenACCToLLVMIRTranslation.h - OpenACC Dialect to LLVM IR -- C++ -*-===//
//
// 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 provides registration calls for OpenACC dialect to LLVM IR translation.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_TARGET_LLVMIR_DIALECT_OPENACC_OPENACCTOLLVMIRTRANSLATION_H
#define MLIR_TARGET_LLVMIR_DIALECT_OPENACC_OPENACCTOLLVMIRTRANSLATION_H
namespace mlir {
class DialectRegistry;
class MLIRContext;
/// Register the OpenACC dialect and the translation to the LLVM IR in
/// the given registry;
void registerOpenACCDialectTranslation(DialectRegistry &registry);
/// Register the OpenACC dialect and the translation in the registry
/// associated with the given context.
void registerOpenACCDialectTranslation(MLIRContext &context);
} // namespace mlir
#endif // MLIR_TARGET_LLVMIR_DIALECT_OPENACC_OPENACCTOLLVMIRTRANSLATION_H

View File

@ -27,6 +27,7 @@ add_mlir_translation_library(MLIRTargetLLVMIRExport
LINK_LIBS PUBLIC
MLIRLLVMIR
MLIROpenACC
MLIROpenMP
MLIRLLVMIRTransforms
MLIRTranslation
@ -42,6 +43,7 @@ add_mlir_translation_library(MLIRToLLVMIRTranslationRegistration
MLIRX86VectorToLLVMIRTranslation
MLIRLLVMToLLVMIRTranslation
MLIRNVVMToLLVMIRTranslation
MLIROpenACCToLLVMIRTranslation
MLIROpenMPToLLVMIRTranslation
MLIRROCDLToLLVMIRTranslation
)

View File

@ -3,6 +3,7 @@ add_subdirectory(ArmSVE)
add_subdirectory(AMX)
add_subdirectory(LLVMIR)
add_subdirectory(NVVM)
add_subdirectory(OpenACC)
add_subdirectory(OpenMP)
add_subdirectory(ROCDL)
add_subdirectory(X86Vector)

View File

@ -0,0 +1,14 @@
add_mlir_translation_library(MLIROpenACCToLLVMIRTranslation
OpenACCToLLVMIRTranslation.cpp
LINK_COMPONENTS
Core
LINK_LIBS PUBLIC
MLIRIR
MLIRLLVMIR
MLIROpenACC
MLIROpenACCToLLVM
MLIRSupport
MLIRTargetLLVMIRExport
)

View File

@ -0,0 +1,304 @@
//===- 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 &registry) {
registry.insert<acc::OpenACCDialect>();
registry.addDialectInterface<acc::OpenACCDialect,
OpenACCDialectLLVMIRTranslationInterface>();
}
void mlir::registerOpenACCDialectTranslation(MLIRContext &context) {
DialectRegistry registry;
registerOpenACCDialectTranslation(registry);
context.appendDialectRegistry(registry);
}

View File

@ -0,0 +1,65 @@
// 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