blob: 3cfa989fb69306c4091cf6bd3a7c37bc4eaa48ef [file] [edit]
//===- ACCDataRuntime.cpp - Emit OpenACC data runtime arguments -*- 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
//
//===----------------------------------------------------------------------===//
//
// Builds the mapping argument arrays used by OpenACC data runtime entry points.
//
//===----------------------------------------------------------------------===//
#include "mlir/Conversion/OpenACCToLLVM/ACCDataRuntime.h"
#include "mlir/Conversion/OpenACCToLLVM/ACCToLLVMUtils.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h"
#include "mlir/Dialect/OpenACC/OpenACC.h"
#include "mlir/Dialect/OpenACC/OpenACCUtils.h"
#include "mlir/Dialect/OpenACC/OpenACCUtilsCG.h"
#include "mlir/Dialect/OpenACC/OpenACCUtilsType.h"
#include "mlir/Transforms/DialectConversion.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVectorExtras.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#define DEBUG_TYPE "acc-data-runtime"
using namespace mlir;
using namespace mlir::acc;
static Value getPointer(Location loc, Value value,
ConversionPatternRewriter &rewriter) {
Type ptrTy = LLVM::LLVMPointerType::get(rewriter.getContext());
if (!value)
return LLVM::ZeroOp::create(rewriter, loc, ptrTy);
if (value.getType() == ptrTy)
return value;
if (isa<PointerLikeType>(value.getType()))
return castPointerLikeTypeIfNeeded(rewriter, loc, value, ptrTy);
Value one = LLVM::ConstantOp::create(rewriter, loc, rewriter.getI32Type(), 1);
Value storage =
LLVM::AllocaOp::create(rewriter, loc, ptrTy, value.getType(), one);
LLVM::StoreOp::create(rewriter, loc, value, storage);
return storage;
}
/// Returns the address of a slot holding \p value, so that the runtime reads
/// the value from memory rather than receiving it directly.
static Value getIndirectPointer(Location loc, Value value,
ConversionPatternRewriter &rewriter) {
Type ptrTy = LLVM::LLVMPointerType::get(rewriter.getContext());
Value one = LLVM::ConstantOp::create(rewriter, loc, rewriter.getI32Type(), 1);
Value storage =
LLVM::AllocaOp::create(rewriter, loc, ptrTy, value.getType(), one);
LLVM::StoreOp::create(rewriter, loc, value, storage);
return storage;
}
Value mlir::createACCDataArray(Location loc, Type elementType,
ArrayRef<Value> values, RewriterBase &rewriter) {
Type ptrTy = LLVM::LLVMPointerType::get(rewriter.getContext());
Type i32Ty = rewriter.getI32Type();
Value count = LLVM::ConstantOp::create(rewriter, loc, i32Ty, values.size());
Value array =
LLVM::AllocaOp::create(rewriter, loc, ptrTy, elementType, count);
for (auto [index, value] : llvm::enumerate(values)) {
Value indexValue = LLVM::ConstantOp::create(rewriter, loc, i32Ty, index);
Value element = LLVM::GEPOp::create(rewriter, loc, ptrTy, elementType,
array, ValueRange{indexValue});
LLVM::StoreOp::create(rewriter, loc, value, element);
}
return array;
}
void mlir::createACCMappedObjectArrays(Location loc,
ArrayRef<ACCMappedObjectArgs> objects,
ACCDataRuntimeArgs &runtimeArgs,
RewriterBase &rewriter) {
Type i32Ty = rewriter.getI32Type();
Type i64Ty = rewriter.getI64Type();
Type ptrTy = LLVM::LLVMPointerType::get(rewriter.getContext());
auto createArray = [&](Type elementType, Value ACCMappedObjectArgs::*field) {
SmallVector<Value> values =
llvm::map_to_vector(objects, [&](const ACCMappedObjectArgs &object) {
return object.*field;
});
return createACCDataArray(loc, elementType, values, rewriter);
};
// The data-call emitter states the count before the object values.
if (!runtimeArgs.argNum)
runtimeArgs.argNum =
LLVM::ConstantOp::create(rewriter, loc, i32Ty, objects.size());
runtimeArgs.argBasePtrs = createArray(ptrTy, &ACCMappedObjectArgs::base);
runtimeArgs.argPtrs = createArray(ptrTy, &ACCMappedObjectArgs::pointer);
runtimeArgs.argSizes = createArray(i64Ty, &ACCMappedObjectArgs::size);
runtimeArgs.argTypes = createArray(i64Ty, &ACCMappedObjectArgs::type);
runtimeArgs.argNames = createArray(ptrTy, &ACCMappedObjectArgs::name);
if (!runtimeArgs.argMappers)
runtimeArgs.argMappers = LLVM::ZeroOp::create(rewriter, loc, ptrTy);
runtimeArgs.argDescs = createArray(ptrTy, &ACCMappedObjectArgs::descriptor);
}
/// Privatized storage only ever exists on the device, so there is no host
/// address to give. A device address names storage the runtime must not look
/// up, and an object mapped by value has no address at all, so both are handed
/// over in a slot the runtime reads them from. A pointer that no clause maps
/// but the device can already access is a device address as well.
Value mlir::createACCMappedObjectPointer(Location loc, Value operand,
Value converted, MapFlags mapFlags,
ConversionPatternRewriter &rewriter) {
Type ptrTy = LLVM::LLVMPointerType::get(rewriter.getContext());
if (isa<PrivateType>(operand.getType()))
return LLVM::ZeroOp::create(rewriter, loc, ptrTy);
if (converted && converted.getType() == ptrTy &&
(bitEnumContainsAny(mapFlags, MapFlags::devptr) ||
(!isDataEntryOp(operand.getDefiningOp()) &&
isDeviceAccessibleValue(operand))))
return getIndirectPointer(loc, converted, rewriter);
return getPointer(loc, converted, rewriter);
}
Value mlir::createACCMappedObjectBase(Location loc, Operation *mapOp,
MapFlags mapFlags,
ConversionPatternRewriter &rewriter) {
Value base;
if (mapOp) {
if (Value attach = acc::getVarPtrPtr(mapOp))
base = remapOrSelf(attach, rewriter);
else if (bitEnumContainsAny(mapFlags, MapFlags::ptr_and_obj))
base = remapOrSelf(acc::getDesc(mapOp), rewriter);
}
return getPointer(loc, base, rewriter);
}
Value mlir::createACCMappedObjectName(Location loc, Value operand,
OpenACCSupport &accSupport,
Region &globalSymbolRegion,
SymbolTable *symbolTable,
ConversionPatternRewriter &rewriter) {
// The runtime resolves an object that lives on the device against the
// symbols of the binary by this name, so it is the name the object is
// emitted under rather than the one the source spells.
VariableNameConfig nameConfig;
nameConfig.preferDemangledName = false;
std::string name = accSupport.getVariableName(operand, nameConfig);
if (name.empty())
return LLVM::ZeroOp::create(
rewriter, loc, LLVM::LLVMPointerType::get(rewriter.getContext()));
return getOrCreateGlobalString(loc, rewriter,
getInternalGlobalName("var_name", name), name,
globalSymbolRegion, symbolTable);
}
static std::optional<MapFlags> computePackedMapFlags(Operation *mapOp) {
if (auto mapInfo = dyn_cast<MapInfoOp>(mapOp))
return mapInfo.getMapFlags();
if (isa<ACC_DATA_ENTRY_OPS>(mapOp))
return computeDataClauseMapFlags(mapOp, hasAttachPoint(mapOp));
return std::nullopt;
}
FailureOr<Value>
mlir::createACCMappedObjectSize(Location loc, Value operand, Value converted,
MapFlags mapFlags, OpenACCSupport &accSupport,
ConversionPatternRewriter &rewriter) {
Operation *scope = rewriter.getInsertionBlock()->getParentOp();
auto module = dyn_cast<ModuleOp>(scope);
if (!module)
module = scope->getParentOfType<ModuleOp>();
// An object mapped by value is passed as the argument itself, so its size is
// the size of what is passed and not of the object it was read from. The two
// differ when the argument holds one field of a decomposed aggregate.
if (bitEnumContainsAny(mapFlags, MapFlags::literal) && converted) {
auto sizeAndAlignment =
acc::getTypeSizeAndAlignment(converted.getType(), module, &accSupport);
if (!sizeAndAlignment) {
std::string message;
llvm::raw_string_ostream os(message);
os << "unhandled type size computation for type: " << converted.getType();
(void)accSupport.emitNYI(loc, message);
return failure();
}
return constantI64(loc, sizeAndAlignment->first.getFixedValue(), rewriter);
}
Operation *mapOp = operand.getDefiningOp();
if (!isDataEntryOp(mapOp))
return constantI64(loc, 0, rewriter);
if (Value size = acc::getMapSize(mapOp))
return castToI64(loc, remapOrSelf(size, rewriter), rewriter);
Value var = acc::getVar(mapOp);
if (!var)
return constantI64(loc, 0, rewriter);
Type varType = acc::getVarType(mapOp);
if (!varType)
varType = var.getType();
// A memref is handed over with a descriptor stating its extents and element
// size, which is what states the size of such a mapping.
if (isa<MemRefType>(var.getType()) || isa<MemRefType>(varType))
return constantI64(loc, 0, rewriter);
// A data clause operation that was not turned into an `acc.map_info` has no
// size of its own, so it is sized the way that operation would have been.
// A size that is not statically known is left to the runtime, as an unsized
// mapping is.
int64_t size =
acc::computeMapInfoSizeBytes(var, varType, acc::getDataDescKind(mapOp),
acc::getBounds(mapOp), &accSupport);
return constantI64(loc, std::max<int64_t>(size, 0), rewriter);
}
static Value getElementSize(Operation *mapOp, OpenACCSupport &accSupport,
ConversionPatternRewriter &rewriter) {
Location loc = mapOp->getLoc();
ModuleOp module = mapOp->getParentOfType<ModuleOp>();
if (std::optional<int64_t> size = acc::getMapElementSize(mapOp))
return constantI64(loc, *size, rewriter);
Type type = acc::getVarType(mapOp);
if (auto shaped = dyn_cast<ShapedType>(type))
type = shaped.getElementType();
if (!type)
return constantI64(loc, 0, rewriter);
if (auto sizeAndAlignment =
acc::getTypeSizeAndAlignment(type, module, &accSupport))
return constantI64(loc, sizeAndAlignment->first.getFixedValue(), rewriter);
return constantI64(loc, 0, rewriter);
}
static Value getBoundValue(Value value, Location loc,
ConversionPatternRewriter &rewriter) {
if (!value)
return constantI64(loc, 0, rewriter);
return castToI64(loc, remapOrSelf(value, rewriter), rewriter);
}
Value mlir::createACCDataDescriptor(Location loc, Value baseDescriptor,
Type baseDescriptorType, uint32_t version,
ValueRange bounds, Value elementSize,
ConversionPatternRewriter &rewriter) {
MLIRContext *context = rewriter.getContext();
Type i8Ty = rewriter.getI8Type();
Type i32Ty = rewriter.getI32Type();
Type i64Ty = rewriter.getI64Type();
Type ptrTy = LLVM::LLVMPointerType::get(context);
Value versionValue = LLVM::ConstantOp::create(
rewriter, loc, i32Ty,
version | static_cast<uint32_t>(
getDataDescriptorKind(DataDescriptor::AccDataDescOpenACC)));
baseDescriptor = LLVM::InsertValueOp::create(
rewriter, loc, baseDescriptorType, baseDescriptor, versionValue,
ArrayRef<int64_t>{0});
Type descriptorType = getDataDescriptorType(
context, DataDescriptor::AccDataDescOpenACC, baseDescriptorType);
Value descriptor = LLVM::ZeroOp::create(rewriter, loc, descriptorType);
Value rank = LLVM::ConstantOp::create(rewriter, loc, i8Ty, bounds.size());
SmallVector<Value> lowerBounds;
SmallVector<Value> upperBounds;
SmallVector<Value> extents;
SmallVector<Value> strides;
SmallVector<Value> starts;
for (Value boundValue : bounds) {
auto bound = cast<DataBoundsOp>(boundValue.getDefiningOp());
lowerBounds.push_back(
getBoundValue(bound.getLowerbound(), bound.getLoc(), rewriter));
upperBounds.push_back(
getBoundValue(bound.getUpperbound(), bound.getLoc(), rewriter));
Value sourceExtent =
bound.getSourceExtent() ? bound.getSourceExtent() : bound.getExtent();
extents.push_back(getBoundValue(sourceExtent, bound.getLoc(), rewriter));
Value stride = getBoundValue(bound.getStride(), bound.getLoc(), rewriter);
if (!bound.getStrideInBytes())
stride =
LLVM::MulOp::create(rewriter, bound.getLoc(), stride, elementSize);
strides.push_back(stride);
starts.push_back(
getBoundValue(bound.getStartIdx(), bound.getLoc(), rewriter));
}
Value lowerBoundsArray =
createACCDataArray(loc, i64Ty, lowerBounds, rewriter);
Value upperBoundsArray =
createACCDataArray(loc, i64Ty, upperBounds, rewriter);
Value extentsArray = createACCDataArray(loc, i64Ty, extents, rewriter);
Value stridesArray = createACCDataArray(loc, i64Ty, strides, rewriter);
Value startsArray = createACCDataArray(loc, i64Ty, starts, rewriter);
using Field = AccDataDescOpenACCField;
auto insert = [&](Value value, Field field) {
descriptor = LLVM::InsertValueOp::create(
rewriter, loc, descriptorType, descriptor, value,
ArrayRef<int64_t>{getDataDescriptorFieldIndex(field)});
};
insert(baseDescriptor, Field::Base);
insert(rank, Field::Rank);
insert(elementSize, Field::ElementSize);
insert(lowerBoundsArray, Field::LowerBounds);
insert(upperBoundsArray, Field::UpperBounds);
insert(extentsArray, Field::Extents);
insert(stridesArray, Field::StridesInBytes);
insert(startsArray, Field::StartIndices);
Value one = LLVM::ConstantOp::create(rewriter, loc, i32Ty, 1);
Value storage =
LLVM::AllocaOp::create(rewriter, loc, ptrTy, descriptorType, one);
LLVM::StoreOp::create(rewriter, loc, descriptor, storage);
return storage;
}
Value mlir::createACCMemRefDescriptorWrapperArg(
Location loc, MemRefType memrefType, Value convertedMemref,
ValueRange bounds, Value elementSize, ConversionPatternRewriter &rewriter) {
MLIRContext *context = rewriter.getContext();
Type i8Ty = rewriter.getI8Type();
Type i32Ty = rewriter.getI32Type();
Type ptrTy = LLVM::LLVMPointerType::get(context);
using Field = AccDataDescMemRefField;
Type descriptorType =
getDataDescriptorType(context, DataDescriptor::AccDataDescMemRef);
auto version = static_cast<uint32_t>(
getDataDescriptorKind(DataDescriptor::AccDataDescMemRef));
Value descriptor = LLVM::ZeroOp::create(rewriter, loc, descriptorType);
Value rank =
LLVM::ConstantOp::create(rewriter, loc, i8Ty, memrefType.getRank());
auto insert = [&](Value value, Field field) {
descriptor = LLVM::InsertValueOp::create(
rewriter, loc, descriptorType, descriptor, value,
ArrayRef<int64_t>{getDataDescriptorFieldIndex(field)});
};
insert(rank, Field::Rank);
insert(elementSize, Field::ElementSize);
insert(getPointer(loc, convertedMemref, rewriter), Field::MemRefDescriptor);
if (!bounds.empty())
return createACCDataDescriptor(loc, descriptor, descriptorType, version,
bounds, elementSize, rewriter);
insert(LLVM::ConstantOp::create(rewriter, loc, i32Ty, version),
Field::Version);
Value one = LLVM::ConstantOp::create(rewriter, loc, i32Ty, 1);
Value storage =
LLVM::AllocaOp::create(rewriter, loc, ptrTy, descriptorType, one);
LLVM::StoreOp::create(rewriter, loc, descriptor, storage);
return storage;
}
Value mlir::createACCArgumentDescriptor(Operation *mapOp,
Value convertedOperand,
OpenACCSupport &accSupport,
ConversionPatternRewriter &rewriter) {
Location loc = mapOp->getLoc();
MLIRContext *context = rewriter.getContext();
Type i32Ty = rewriter.getI32Type();
Type ptrTy = LLVM::LLVMPointerType::get(context);
SmallVector<Value> bounds = acc::getBounds(mapOp);
DataDescKind descKind = acc::getDataDescKind(mapOp);
// Only a descriptor stating bounds reads the element size, so it is
// materialized where it is used, leaving no constant behind for a mapping
// that needs no descriptor at all.
auto elementSize = [&] {
return getElementSize(mapOp, accSupport, rewriter);
};
Value var = acc::getVar(mapOp);
if (var && isa<MemRefType>(var.getType())) {
auto memrefType = cast<MemRefType>(var.getType());
return createACCMemRefDescriptorWrapperArg(
loc, memrefType, convertedOperand, bounds, elementSize(), rewriter);
}
if (bitEnumContainsAny(descKind, DataDescKind::cfi)) {
using Field = AccDataDescCFIField;
Type descriptorType =
getDataDescriptorType(context, DataDescriptor::AccDataDescCFI);
auto version = static_cast<uint32_t>(
getDataDescriptorKind(DataDescriptor::AccDataDescCFI));
Value descriptor = LLVM::ZeroOp::create(rewriter, loc, descriptorType);
Value descriptorStorage = remapOrSelf(acc::getDesc(mapOp), rewriter);
if (!descriptorStorage)
descriptorStorage = convertedOperand;
descriptor = LLVM::InsertValueOp::create(
rewriter, loc, descriptorType, descriptor,
getPointer(loc, descriptorStorage, rewriter),
ArrayRef<int64_t>{getDataDescriptorFieldIndex(Field::CFIDescriptor)});
if (!bounds.empty())
return createACCDataDescriptor(loc, descriptor, descriptorType, version,
bounds, elementSize(), rewriter);
Value versionValue =
LLVM::ConstantOp::create(rewriter, loc, i32Ty, version);
descriptor = LLVM::InsertValueOp::create(
rewriter, loc, descriptorType, descriptor, versionValue,
ArrayRef<int64_t>{getDataDescriptorFieldIndex(Field::Version)});
Value one = LLVM::ConstantOp::create(rewriter, loc, i32Ty, 1);
Value storage =
LLVM::AllocaOp::create(rewriter, loc, ptrTy, descriptorType, one);
LLVM::StoreOp::create(rewriter, loc, descriptor, storage);
return storage;
}
if (!bounds.empty()) {
Type descriptorType =
getDataDescriptorType(context, DataDescriptor::AccDataDescGeneric);
auto version = static_cast<uint32_t>(
getDataDescriptorKind(DataDescriptor::AccDataDescGeneric));
Value descriptor = LLVM::ZeroOp::create(rewriter, loc, descriptorType);
return createACCDataDescriptor(loc, descriptor, descriptorType, version,
bounds, elementSize(), rewriter);
}
return LLVM::ZeroOp::create(rewriter, loc, ptrTy);
}
SmallVector<Value> mlir::ACCDataRuntimeArgs::getCallArgs() const {
return {ident, flags, deviceType, argNum, argBasePtrs, argPtrs,
argSizes, argTypes, argNames, argMappers, argDescs};
}
LogicalResult mlir::emitACCDataRuntimeArgs(
Location loc, ValueRange mappingOperands, ValueRange convertedOperands,
ConversionPatternRewriter &rewriter, Region &globalSymbolRegion,
acc::OpenACCSupport &accSupport, const acc::ACCRuntimeCallConfig &config,
ACCDataRuntimeArgs &runtimeArgs, const ACCDataCallOptions &options,
SymbolTable *symbolTable) {
if (mappingOperands.size() != convertedOperands.size())
return failure();
runtimeArgs.ident =
createIdent(loc, getParentFunctionName(mappingOperands), rewriter,
globalSymbolRegion, config, symbolTable);
runtimeArgs.flags = constantI64(loc, options.flags, rewriter);
runtimeArgs.deviceType = constantI64(
loc, config.getDeviceTypeRuntimeValue(DeviceType::None), rewriter);
runtimeArgs.argMappers = LLVM::ZeroOp::create(
rewriter, loc, LLVM::LLVMPointerType::get(rewriter.getContext()));
// An aggregate passed by value is decomposed into one argument per field by
// the target ABI, so the runtime is told about one object per field as well.
// Each of them keeps the map entry of the aggregate it came from. The device
// address of an aggregate is returned in the slot of its first field.
struct MappedObject {
Value operand;
Value converted;
bool firstField;
};
SmallVector<MappedObject> mappedObjects;
for (auto [operand, converted] :
llvm::zip_equal(mappingOperands, convertedOperands)) {
auto structType = dyn_cast<LLVM::LLVMStructType>(converted.getType());
if (structType && !structType.isIdentified() &&
structType.getBody().size() > 1) {
for (unsigned field = 0, fields = structType.getBody().size();
field != fields; ++field)
mappedObjects.push_back(
{operand,
LLVM::ExtractValueOp::create(rewriter, loc, converted, field),
field == 0});
continue;
}
mappedObjects.push_back({operand, converted, true});
}
// The count precedes the values of the objects, which is where a decomposed
// aggregate states how many fields it became.
runtimeArgs.argNum = LLVM::ConstantOp::create(
rewriter, loc, rewriter.getI32Type(), mappedObjects.size());
constexpr unsigned noSlot = ~0u;
runtimeArgs.returnAddressSlots.assign(options.returnAddressOperands.size(),
noSlot);
SmallVector<ACCMappedObjectArgs> objects;
for (auto [slot, object] : llvm::enumerate(mappedObjects)) {
auto [operand, converted, firstField] = object;
Operation *mapOp = operand.getDefiningOp();
if (!isDataEntryOp(mapOp))
return failure();
std::optional<MapFlags> mapFlags = computePackedMapFlags(mapOp);
if (!mapFlags)
return failure();
*mapFlags = config.postProcessMapFlags(mapOp, *mapFlags);
LLVM_DEBUG(llvm::dbgs() << "mapping " << *mapOp << "\n as "
<< config.formatMapFlags(*mapFlags) << "\n");
Location mapLoc = mapOp->getLoc();
ACCMappedObjectArgs args;
args.pointer = createACCMappedObjectPointer(mapLoc, operand, converted,
*mapFlags, rewriter);
// An object that only lives on the device has no host address to state as
// its base.
args.base =
bitEnumContainsAny(*mapFlags, MapFlags::device_resident)
? getPointer(mapLoc, Value(), rewriter)
: createACCMappedObjectBase(mapLoc, mapOp, *mapFlags, rewriter);
// A local object that lives on the device is torn down when the region
// that declared it ends, which the runtime only does for a mapping that
// states it is deleted.
if (options.callKind == ACCDataCallKind::DataExit &&
bitEnumContainsAny(*mapFlags, MapFlags::device_resident)) {
Value var = acc::getVar(mapOp);
if (var &&
!isa_and_nonnull<AddressOfGlobalOpInterface>(var.getDefiningOp()))
*mapFlags = *mapFlags | MapFlags::delete_;
}
if (firstField) {
for (auto [index, returned] :
llvm::enumerate(options.returnAddressOperands)) {
if (returned != operand)
continue;
*mapFlags = *mapFlags | MapFlags::return_param;
runtimeArgs.returnAddressSlots[index] = slot;
}
}
int64_t mapType = config.getMapFlagsRuntimeValue(*mapFlags);
FailureOr<Value> size = createACCMappedObjectSize(
mapLoc, operand, converted, *mapFlags, accSupport, rewriter);
if (failed(size))
return failure();
args.size = *size;
args.type = constantI64(mapLoc, mapType, rewriter);
args.name = createACCMappedObjectName(
mapLoc, operand, accSupport, globalSymbolRegion, symbolTable, rewriter);
args.descriptor =
createACCArgumentDescriptor(mapOp, converted, accSupport, rewriter);
objects.push_back(args);
}
if (llvm::is_contained(runtimeArgs.returnAddressSlots, noSlot))
return failure();
createACCMappedObjectArrays(loc, objects, runtimeArgs, rewriter);
return success();
}
FailureOr<SmallVector<Value>> mlir::emitACCDataRuntimeCall(
Location loc, RuntimeFunction fn, ValueRange mappingOperands,
ValueRange convertedOperands, ConversionPatternRewriter &rewriter,
OpenACCSupport &accSupport, Region &globalSymbolRegion,
SymbolTable &symbolTable, const ACCRuntimeCallConfig &config,
ArrayRef<Value> trailingArguments, const ACCDataCallOptions &options) {
SmallVector<Value> returnedAddresses;
if (mappingOperands.empty())
return returnedAddresses;
ACCDataRuntimeArgs runtimeArgs;
if (failed(emitACCDataRuntimeArgs(
loc, mappingOperands, convertedOperands, rewriter, globalSymbolRegion,
accSupport, config, runtimeArgs, options, &symbolTable)))
return rewriter.notifyMatchFailure(
mappingOperands.front().getDefiningOp(),
"unsupported OpenACC data-clause operand");
SmallVector<Value> arguments = runtimeArgs.getCallArgs();
arguments.append(trailingArguments.begin(), trailingArguments.end());
if (failed(createRuntimeCall(loc, rewriter, globalSymbolRegion, symbolTable,
fn, config, arguments)))
return failure();
Type i32Ty = rewriter.getI32Type();
Type ptrTy = LLVM::LLVMPointerType::get(rewriter.getContext());
for (unsigned slot : runtimeArgs.returnAddressSlots) {
Value index = LLVM::ConstantOp::create(rewriter, loc, i32Ty, slot);
Value element =
LLVM::GEPOp::create(rewriter, loc, ptrTy, ptrTy,
runtimeArgs.argBasePtrs, ValueRange{index});
returnedAddresses.push_back(
LLVM::LoadOp::create(rewriter, loc, ptrTy, element));
}
return returnedAddresses;
}