| //===- ACCKernelLaunchPatterns.cpp - Kernel launch to LLVM ------*- 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // Lower acc.kernel_environment and the gpu.launch_func operations in its body | |
| // together. The environment establishes one implicit data region. Its entry | |
| // call returns the device addresses that every contained kernel launch uses. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "mlir/Conversion/OpenACCToLLVM/ACCDataRuntime.h" | |
| #include "mlir/Conversion/OpenACCToLLVM/ACCToLLVM.h" | |
| #include "mlir/Conversion/OpenACCToLLVM/ACCToLLVMUtils.h" | |
| #include "mlir/Conversion/LLVMCommon/Pattern.h" | |
| #include "mlir/Dialect/Func/IR/FuncOps.h" | |
| #include "mlir/Dialect/GPU/IR/GPUDialect.h" | |
| #include "mlir/Dialect/LLVMIR/LLVMDialect.h" | |
| #include "mlir/Dialect/OpenACC/Analysis/OpenACCSupport.h" | |
| #include "mlir/Dialect/OpenACC/OpenACC.h" | |
| #include "mlir/Dialect/OpenACC/OpenACCUtilsCG.h" | |
| #include "mlir/Dialect/OpenACC/OpenACCUtilsType.h" | |
| #include "mlir/IR/BuiltinOps.h" | |
| #include "llvm/ADT/DenseMap.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include <array> | |
| using namespace mlir; | |
| using namespace mlir::acc; | |
| static bool isDevicePtrMapping(Value operand) { | |
| Operation *mapOp = operand.getDefiningOp(); | |
| if (!mapOp) | |
| return false; | |
| if (std::optional<MapFlags> flags = getMapFlags(mapOp)) | |
| return bitEnumContainsAny(*flags, MapFlags::devptr); | |
| if (std::optional<DataClause> clause = getDataClause(mapOp)) | |
| return *clause == DataClause::acc_deviceptr; | |
| return false; | |
| } | |
| static bool isDataEntryInsideEnvironment(KernelEnvironmentOp environment, | |
| Value operand) { | |
| Operation *mapOp = operand.getDefiningOp(); | |
| return isDataEntryOp(mapOp) && environment->isAncestor(mapOp); | |
| } | |
| /// The flags of a data clause operation that maps a kernel argument. A kernel | |
| /// argument only looks up the mapping its clause established, so the clauses | |
| /// that establish or tear down something else contribute no flag for it. | |
| static MapFlags getKernelDataClauseMapFlags(Operation *entryOp) { | |
| bool ptrAndObj = hasAttachPoint(entryOp); | |
| MapFlags ptrAndObjFlag = ptrAndObj ? MapFlags::ptr_and_obj : MapFlags::none; | |
| std::optional<DataClause> clause = getDataClause(entryOp); | |
| if (clause == DataClause::acc_declare_device_resident) | |
| return MapFlags::present | ptrAndObjFlag; | |
| if (clause == DataClause::acc_attach || clause == DataClause::acc_detach) | |
| return ptrAndObjFlag; | |
| return bitEnumClear(computeDataClauseMapFlags(entryOp, ptrAndObj), | |
| MapFlags::device_resident); | |
| } | |
| /// The flags a kernel argument is passed with. An operand the environment | |
| /// already mapped is passed as the device address its entry call returned. An | |
| /// operand that no clause maps is passed by value. | |
| static MapFlags getKernelArgMapFlags(Value operand, bool alreadyMapped, | |
| const ACCRuntimeCallConfig &config) { | |
| Operation *mapOp = operand.getDefiningOp(); | |
| if (!isDataEntryOp(mapOp)) | |
| return mapOp ? config.postProcessMapFlags(mapOp, MapFlags::literal) | |
| : MapFlags::literal; | |
| MapFlags flags; | |
| if (alreadyMapped) | |
| flags = MapFlags::devptr | MapFlags::literal; | |
| else if (auto mapInfo = dyn_cast<MapInfoOp>(mapOp)) | |
| flags = mapInfo.getMapFlags(); | |
| else | |
| flags = getKernelDataClauseMapFlags(mapOp); | |
| if (getImplicitFlag(mapOp)) | |
| flags = flags | MapFlags::implicit; | |
| return config.postProcessMapFlags(mapOp, flags); | |
| } | |
| /// The descriptor of a kernel argument. The device address the environment | |
| /// returned is passed as is, since the runtime would map an address that | |
| /// carries a descriptor again. | |
| static FailureOr<Value> | |
| getKernelArgDescriptor(Location loc, Value operand, Value converted, | |
| bool alreadyMapped, OpenACCSupport &accSupport, | |
| ConversionPatternRewriter &rewriter) { | |
| Type ptrTy = LLVM::LLVMPointerType::get(rewriter.getContext()); | |
| Operation *mapOp = operand.getDefiningOp(); | |
| if (alreadyMapped) | |
| return Value(LLVM::ZeroOp::create(rewriter, loc, ptrTy)); | |
| if (isDataEntryOp(mapOp)) | |
| return createACCArgumentDescriptor(mapOp, converted, accSupport, rewriter); | |
| if (auto memrefType = dyn_cast<MemRefType>(operand.getType())) { | |
| ModuleOp module = rewriter.getInsertionBlock() | |
| ->getParentOp() | |
| ->getParentOfType<ModuleOp>(); | |
| auto sizeAndAlignment = acc::getTypeSizeAndAlignment( | |
| memrefType.getElementType(), module, &accSupport); | |
| if (!sizeAndAlignment) { | |
| std::string message; | |
| llvm::raw_string_ostream os(message); | |
| os << "unhandled type size computation for type: " | |
| << memrefType.getElementType(); | |
| (void)accSupport.emitNYI(loc, message); | |
| return failure(); | |
| } | |
| return createACCMemRefDescriptorWrapperArg( | |
| loc, memrefType, converted, /*bounds=*/{}, | |
| constantI64(loc, sizeAndAlignment->first.getFixedValue(), rewriter), | |
| rewriter); | |
| } | |
| return Value(LLVM::ZeroOp::create(rewriter, loc, ptrTy)); | |
| } | |
| /// Emits the argument arrays of the kernel launched by \p launch, one entry per | |
| /// kernel operand. Each operand is one parameter of the kernel. | |
| static FailureOr<ACCDataRuntimeArgs> emitKernelRuntimeArgs( | |
| gpu::LaunchFuncOp launch, gpu::LaunchFuncOpAdaptor adaptor, | |
| ValueRange alreadyMappedOperands, ConversionPatternRewriter &rewriter, | |
| OpenACCSupport &accSupport, Region &globalSymbolRegion, | |
| SymbolTable &symbolTable, const ACCRuntimeCallConfig &config) { | |
| Location loc = launch.getLoc(); | |
| SmallVector<ACCMappedObjectArgs> objects; | |
| for (auto [operand, converted] : llvm::zip_equal( | |
| launch.getKernelOperands(), adaptor.getKernelOperands())) { | |
| bool alreadyMapped = llvm::is_contained(alreadyMappedOperands, operand); | |
| MapFlags mapFlags = getKernelArgMapFlags(operand, alreadyMapped, config); | |
| Operation *mapOp = operand.getDefiningOp(); | |
| ACCMappedObjectArgs args; | |
| // A kernel argument states the base of the mapping it looks up, which for | |
| // an object the declare directive made resident is that of its entry. | |
| args.base = createACCMappedObjectBase( | |
| loc, isDataEntryOp(mapOp) ? mapOp : nullptr, mapFlags, rewriter); | |
| args.pointer = createACCMappedObjectPointer(loc, operand, converted, | |
| mapFlags, rewriter); | |
| FailureOr<Value> size = createACCMappedObjectSize( | |
| loc, operand, converted, mapFlags, accSupport, rewriter); | |
| if (failed(size)) | |
| return failure(); | |
| args.size = *size; | |
| args.type = | |
| constantI64(loc, config.getMapFlagsRuntimeValue(mapFlags), rewriter); | |
| args.name = createACCMappedObjectName( | |
| loc, operand, accSupport, globalSymbolRegion, &symbolTable, rewriter); | |
| FailureOr<Value> descriptor = getKernelArgDescriptor( | |
| loc, operand, converted, alreadyMapped, accSupport, rewriter); | |
| if (failed(descriptor)) | |
| return failure(); | |
| args.descriptor = *descriptor; | |
| objects.push_back(args); | |
| } | |
| ACCDataRuntimeArgs runtimeArgs; | |
| createACCMappedObjectArrays(loc, objects, runtimeArgs, rewriter); | |
| return runtimeArgs; | |
| } | |
| static FailureOr<Value> createKernelArgumentStruct( | |
| gpu::LaunchFuncOp launch, gpu::LaunchFuncOpAdaptor adaptor, | |
| ValueRange alreadyMappedOperands, ConversionPatternRewriter &rewriter, | |
| OpenACCSupport &accSupport, Region &globalSymbolRegion, | |
| SymbolTable &symbolTable, const ACCRuntimeCallConfig &config) { | |
| FailureOr<ACCDataRuntimeArgs> emitted = emitKernelRuntimeArgs( | |
| launch, adaptor, alreadyMappedOperands, rewriter, accSupport, | |
| globalSymbolRegion, symbolTable, config); | |
| if (failed(emitted)) | |
| return failure(); | |
| ACCDataRuntimeArgs runtimeArgs = *emitted; | |
| Location loc = launch.getLoc(); | |
| MLIRContext *context = rewriter.getContext(); | |
| Type i32Ty = rewriter.getI32Type(); | |
| Type ptrTy = LLVM::LLVMPointerType::get(context); | |
| Type argsType = getKernelArgumentType(context); | |
| Value args = LLVM::ZeroOp::create(rewriter, loc, argsType); | |
| auto insert = [&](Value value, KernelArgumentField field) { | |
| args = LLVM::InsertValueOp::create( | |
| rewriter, loc, argsType, args, value, | |
| ArrayRef<int64_t>{getKernelArgumentFieldIndex(field)}); | |
| }; | |
| insert(runtimeArgs.argNum, KernelArgumentField::ArgNum); | |
| insert(runtimeArgs.argBasePtrs, KernelArgumentField::ArgBasePtrs); | |
| insert(runtimeArgs.argPtrs, KernelArgumentField::ArgPtrs); | |
| insert(runtimeArgs.argSizes, KernelArgumentField::ArgSizes); | |
| insert(runtimeArgs.argTypes, KernelArgumentField::ArgTypes); | |
| insert(runtimeArgs.argNames, KernelArgumentField::ArgNames); | |
| insert(runtimeArgs.argMappers, KernelArgumentField::ArgMappers); | |
| insert(runtimeArgs.argDescs, KernelArgumentField::ArgDescs); | |
| // The vector length is the first block dimension. The worker count is the | |
| // product of the remaining ones. | |
| Value blockY = castToI64(loc, adaptor.getBlockSizeY(), rewriter); | |
| Value blockZ = castToI64(loc, adaptor.getBlockSizeZ(), rewriter); | |
| insert(LLVM::MulOp::create(rewriter, loc, blockY, blockZ), | |
| KernelArgumentField::NumWorkers); | |
| insert(castToI64(loc, adaptor.getBlockSizeX(), rewriter), | |
| KernelArgumentField::VectorLength); | |
| Value dynamicSharedMemorySize = | |
| adaptor.getDynamicSharedMemorySize() | |
| ? castToI64(loc, adaptor.getDynamicSharedMemorySize(), rewriter) | |
| : constantI64(loc, 0, rewriter); | |
| insert(dynamicSharedMemorySize, KernelArgumentField::SmemSize); | |
| // The grid sizes are the entries of the array in the NumGangs field. | |
| int64_t numGangsField = | |
| getKernelArgumentFieldIndex(KernelArgumentField::NumGangs); | |
| std::array<Value, 3> gridSizes = { | |
| adaptor.getGridSizeX(), adaptor.getGridSizeY(), adaptor.getGridSizeZ()}; | |
| for (auto [dimension, gridSize] : llvm::enumerate(gridSizes)) | |
| args = LLVM::InsertValueOp::create( | |
| rewriter, loc, argsType, args, castToI64(loc, gridSize, rewriter), | |
| ArrayRef<int64_t>{numGangsField, static_cast<int64_t>(dimension)}); | |
| Value one = LLVM::ConstantOp::create(rewriter, loc, i32Ty, 1); | |
| Value argsPtr = LLVM::AllocaOp::create(rewriter, loc, ptrTy, argsType, one); | |
| LLVM::StoreOp::create(rewriter, loc, args, argsPtr); | |
| return argsPtr; | |
| } | |
| static FailureOr<Value> getKernelPointer(gpu::LaunchFuncOp launch, | |
| ConversionPatternRewriter &rewriter, | |
| Region &globalSymbolRegion, | |
| SymbolTable &symbolTable) { | |
| Location loc = launch.getLoc(); | |
| Type ptrTy = LLVM::LLVMPointerType::get(rewriter.getContext()); | |
| StringRef kernelName = launch.getKernel().getLeafReference().getValue(); | |
| if (auto function = symbolTable.lookup<LLVM::LLVMFuncOp>(kernelName)) | |
| return LLVM::AddressOfOp::create(rewriter, loc, ptrTy, function.getName()) | |
| .getResult(); | |
| if (auto function = symbolTable.lookup<func::FuncOp>(kernelName)) | |
| return LLVM::AddressOfOp::create(rewriter, loc, ptrTy, function.getName()) | |
| .getResult(); | |
| auto gpuFunction = SymbolTable::lookupNearestSymbolFrom<gpu::GPUFuncOp>( | |
| launch, launch.getKernel()); | |
| if (!gpuFunction) | |
| return failure(); | |
| { | |
| OpBuilder::InsertionGuard guard(rewriter); | |
| rewriter.setInsertionPointToStart(&globalSymbolRegion.front()); | |
| auto functionType = LLVM::LLVMFunctionType::get( | |
| LLVM::LLVMVoidType::get(rewriter.getContext()), {}); | |
| auto declaration = | |
| LLVM::LLVMFuncOp::create(rewriter, loc, kernelName, functionType); | |
| symbolTable.insert(declaration); | |
| } | |
| return LLVM::AddressOfOp::create(rewriter, loc, ptrTy, kernelName) | |
| .getResult(); | |
| } | |
| static void declareLibc(SymbolTable &symbolTable, Region &globalSymbolRegion, | |
| Location loc, StringRef name, Type result, | |
| ArrayRef<Type> arguments) { | |
| if (symbolTable.lookup<LLVM::LLVMFuncOp>(name)) | |
| return; | |
| OpBuilder builder = OpBuilder::atBlockEnd(&globalSymbolRegion.front()); | |
| auto functionType = LLVM::LLVMFunctionType::get(result, arguments); | |
| symbolTable.insert( | |
| LLVM::LLVMFuncOp::create(builder, loc, name, functionType)); | |
| } | |
| /// The kernel entry point reports a status other than success when the kernel | |
| /// could not run on the device. The program is then meant to run the region on | |
| /// the host, which is not implemented, so the failure is reported and the | |
| /// program stops. | |
| static void emitHostFallbackNotImplemented(Location loc, Value status, | |
| ConversionPatternRewriter &rewriter, | |
| Region &globalSymbolRegion, | |
| SymbolTable &symbolTable) { | |
| MLIRContext *context = rewriter.getContext(); | |
| Type i32Ty = rewriter.getI32Type(); | |
| Type ptrTy = LLVM::LLVMPointerType::get(context); | |
| Type voidTy = LLVM::LLVMVoidType::get(context); | |
| Block *preBlock = rewriter.getInsertionBlock(); | |
| Block *continueBlock = | |
| rewriter.splitBlock(preBlock, rewriter.getInsertionPoint()); | |
| Block *errorBlock = | |
| rewriter.createBlock(preBlock->getParent(), continueBlock->getIterator()); | |
| rewriter.setInsertionPointToEnd(preBlock); | |
| Value success = LLVM::ConstantOp::create( | |
| rewriter, loc, i32Ty, static_cast<int32_t>(RuntimeResult::Success)); | |
| Value failed = LLVM::ICmpOp::create(rewriter, loc, LLVM::ICmpPredicate::ne, | |
| status, success); | |
| LLVM::CondBrOp::create(rewriter, loc, failed, errorBlock, ValueRange{}, | |
| continueBlock, ValueRange{}); | |
| // TODO: Runtime API should provide a mechanism to report this error. | |
| rewriter.setInsertionPointToStart(errorBlock); | |
| declareLibc(symbolTable, globalSymbolRegion, loc, "puts", i32Ty, {ptrTy}); | |
| declareLibc(symbolTable, globalSymbolRegion, loc, "fflush", i32Ty, {ptrTy}); | |
| declareLibc(symbolTable, globalSymbolRegion, loc, "abort", voidTy, {}); | |
| Value message = getOrCreateGlobalString( | |
| loc, rewriter, getInternalGlobalName("message", "host_fallback_nyi"), | |
| "host fallback execution is not implemented\n", globalSymbolRegion, | |
| &symbolTable); | |
| LLVM::CallOp::create(rewriter, loc, TypeRange{i32Ty}, | |
| SymbolRefAttr::get(context, "puts"), | |
| ValueRange{message}); | |
| Value nullFile = LLVM::ZeroOp::create(rewriter, loc, ptrTy); | |
| LLVM::CallOp::create(rewriter, loc, TypeRange{i32Ty}, | |
| SymbolRefAttr::get(context, "fflush"), | |
| ValueRange{nullFile}); | |
| LLVM::CallOp::create(rewriter, loc, TypeRange{}, | |
| SymbolRefAttr::get(context, "abort"), ValueRange{}); | |
| LLVM::UnreachableOp::create(rewriter, loc); | |
| rewriter.setInsertionPointToStart(continueBlock); | |
| } | |
| static LogicalResult | |
| emitKernelCall(gpu::LaunchFuncOp launch, gpu::LaunchFuncOpAdaptor adaptor, | |
| Value asyncQueue, ValueRange alreadyMappedOperands, | |
| ConversionPatternRewriter &rewriter, OpenACCSupport &accSupport, | |
| Region &globalSymbolRegion, SymbolTable &symbolTable, | |
| const ACCRuntimeCallConfig &config) { | |
| Location loc = launch.getLoc(); | |
| FailureOr<Value> kernelPointer = | |
| getKernelPointer(launch, rewriter, globalSymbolRegion, symbolTable); | |
| if (failed(kernelPointer)) { | |
| (void)accSupport.emitNYI(loc, "undefined kernel"); | |
| return failure(); | |
| } | |
| FailureOr<Value> arguments = createKernelArgumentStruct( | |
| launch, adaptor, alreadyMappedOperands, rewriter, accSupport, | |
| globalSymbolRegion, symbolTable, config); | |
| if (failed(arguments)) | |
| return failure(); | |
| StringRef kernelName = launch.getKernel().getLeafReference().getValue(); | |
| Value kernelNamePtr = getOrCreateGlobalString( | |
| loc, rewriter, getInternalGlobalName("kernel_name", kernelName), | |
| kernelName, globalSymbolRegion, &symbolTable); | |
| Value ident = createIdent(loc, getParentFunctionName(launch), rewriter, | |
| globalSymbolRegion, config, &symbolTable); | |
| // The kernel call uses the explicit data-region flag. | |
| Value flags = constantI64(loc, static_cast<int64_t>(ACCDataRegionFlags::None), | |
| rewriter); | |
| // The launch is made for the default device type, as the data calls of the | |
| // environment are. | |
| Value deviceType = constantI64( | |
| loc, config.getDeviceTypeRuntimeValue(DeviceType::None), rewriter); | |
| Value binaryDescriptor = config.createKernelBinaryDescriptor(loc, rewriter); | |
| FailureOr<LLVM::CallOp> call = | |
| createRuntimeCall(loc, rewriter, globalSymbolRegion, symbolTable, | |
| RuntimeFunction::ACCRTL_tgt_acc_kernel, config, | |
| {ident, *kernelPointer, flags, deviceType, *arguments, | |
| asyncQueue, kernelNamePtr, binaryDescriptor}); | |
| if (failed(call)) | |
| return failure(); | |
| emitHostFallbackNotImplemented(loc, call->getResult(), rewriter, | |
| globalSymbolRegion, symbolTable); | |
| return success(); | |
| } | |
| namespace { | |
| struct KernelEnvironmentLowering | |
| : public ConvertOpToLLVMPattern<KernelEnvironmentOp> { | |
| KernelEnvironmentLowering(const LLVMTypeConverter &converter, | |
| OpenACCSupport &accSupport, | |
| Region &globalSymbolRegion, | |
| SymbolTable &symbolTable, | |
| const ACCRuntimeCallConfig &config, | |
| DeviceType clauseDeviceType, | |
| PatternBenefit benefit = 1) | |
| : ConvertOpToLLVMPattern<KernelEnvironmentOp>(converter, benefit), | |
| accSupport(accSupport), globalSymbolRegion(globalSymbolRegion), | |
| symbolTable(symbolTable), config(config), | |
| clauseDeviceType(clauseDeviceType) {} | |
| LogicalResult | |
| matchAndRewrite(KernelEnvironmentOp environment, OpAdaptor adaptor, | |
| ConversionPatternRewriter &rewriter) const override { | |
| ValueRange mappingOperands = environment.getDataClauseOperands(); | |
| ValueRange convertedMappingOperands = adaptor.getDataClauseOperands(); | |
| Location beginLoc = mappingOperands.empty() | |
| ? environment.getLoc() | |
| : mappingOperands.front().getLoc(); | |
| Location endLoc = getMappingExitLoc(mappingOperands).value_or(beginLoc); | |
| Value asyncQueue = | |
| getAsyncQueue(environment, clauseDeviceType, rewriter, config); | |
| if (failed(emitWaitClause(environment, clauseDeviceType, asyncQueue, | |
| rewriter, accSupport, globalSymbolRegion, | |
| symbolTable, config))) | |
| return failure(); | |
| SmallVector<Value> alreadyMappedOperands; | |
| for (Value operand : mappingOperands) | |
| if (!isDevicePtrMapping(operand)) | |
| alreadyMappedOperands.push_back(operand); | |
| ACCDataCallOptions beginOptions; | |
| beginOptions.flags = static_cast<int64_t>(ACCDataRegionFlags::Implicit); | |
| beginOptions.returnAddressOperands = alreadyMappedOperands; | |
| FailureOr<SmallVector<Value>> devicePointers = emitACCDataRuntimeCall( | |
| beginLoc, RuntimeFunction::ACCRTL_tgt_acc_data_begin, mappingOperands, | |
| convertedMappingOperands, rewriter, accSupport, globalSymbolRegion, | |
| symbolTable, config, {asyncQueue}, beginOptions); | |
| if (failed(devicePointers)) | |
| return failure(); | |
| llvm::DenseMap<Value, Value> deviceAddress; | |
| for (auto [operand, pointer] : | |
| llvm::zip_equal(alreadyMappedOperands, *devicePointers)) | |
| deviceAddress[operand] = pointer; | |
| SmallVector<gpu::LaunchFuncOp> launches; | |
| environment.walk( | |
| [&](gpu::LaunchFuncOp launch) { launches.push_back(launch); }); | |
| for (gpu::LaunchFuncOp launch : launches) { | |
| SmallVector<Value> converted; | |
| converted.reserve(launch->getNumOperands()); | |
| for (Value operand : launch->getOperands()) { | |
| Value value; | |
| // A data entry in the region is mapped by this kernel, so it is passed | |
| // as the variable of its clause. | |
| if (isDataEntryInsideEnvironment(environment, operand)) | |
| value = remapOrSelf(getVar(operand.getDefiningOp()), rewriter); | |
| else | |
| value = deviceAddress.lookup(operand); | |
| if (!value) | |
| value = rewriter.getRemappedValue(operand); | |
| if (!value) | |
| return rewriter.notifyMatchFailure(launch, | |
| "unconverted kernel operand"); | |
| converted.push_back(value); | |
| } | |
| gpu::LaunchFuncOpAdaptor launchAdaptor(converted, launch); | |
| Value queue = launchAdaptor.getAsyncObject(); | |
| queue = queue ? castToI64(launch.getLoc(), queue, rewriter) : asyncQueue; | |
| rewriter.setInsertionPoint(launch); | |
| if (failed(emitKernelCall(launch, launchAdaptor, queue, | |
| alreadyMappedOperands, rewriter, accSupport, | |
| globalSymbolRegion, symbolTable, config))) | |
| return failure(); | |
| rewriter.eraseOp(launch); | |
| } | |
| rewriter.setInsertionPointAfter(environment); | |
| ACCDataCallOptions endOptions; | |
| endOptions.callKind = ACCDataCallKind::DataExit; | |
| endOptions.flags = static_cast<int64_t>(ACCDataRegionFlags::Implicit); | |
| if (failed(emitACCDataRuntimeCall( | |
| endLoc, RuntimeFunction::ACCRTL_tgt_acc_data_end, mappingOperands, | |
| convertedMappingOperands, rewriter, accSupport, globalSymbolRegion, | |
| symbolTable, config, {asyncQueue}, endOptions))) | |
| return failure(); | |
| spliceConstructRegion(environment, environment.getRegion(), rewriter); | |
| rewriter.eraseOp(environment); | |
| return success(); | |
| } | |
| OpenACCSupport &accSupport; | |
| Region &globalSymbolRegion; | |
| SymbolTable &symbolTable; | |
| ACCRuntimeCallConfig config; | |
| DeviceType clauseDeviceType; | |
| }; | |
| } // namespace | |
| void mlir::configureACCKernelLaunchConversionLegality( | |
| ConversionTarget &target) { | |
| target.addIllegalOp<KernelEnvironmentOp>(); | |
| } | |
| void mlir::populateACCKernelLaunchPatterns(LLVMTypeConverter &converter, | |
| RewritePatternSet &patterns, | |
| OpenACCSupport &accSupport, | |
| Region &globalSymbolRegion, | |
| SymbolTable &symbolTable, | |
| const ACCRuntimeCallConfig &config, | |
| DeviceType clauseDeviceType) { | |
| patterns.add<KernelEnvironmentLowering>(converter, accSupport, | |
| globalSymbolRegion, symbolTable, | |
| config, clauseDeviceType); | |
| } |