blob: 537c5cea5b38700ebeaae1f538545fa69a822bd3 [file] [edit]
//===- StackToShared.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 various OpenMP dialect utilities.
//
//===----------------------------------------------------------------------===//
#include "mlir/Dialect/OpenMP/Utils/Utils.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
using namespace mlir;
void mlir::omp::setOffloadModuleInterfaceAttributes(
ModuleOp module, const OffloadModuleOpts &opts) {
if (auto offloadMod = llvm::dyn_cast<OffloadModuleInterface>(*module)) {
offloadMod.setIsTargetDevice(opts.isTargetDevice);
offloadMod.setIsGPU(opts.isGPU);
if (opts.forceUSM)
offloadMod.setRequires(offloadMod.getRequires() |
ClauseRequires::unified_shared_memory);
offloadMod.setFlags(opts.targetDebugKind, opts.assumeTeamsOversubscription,
opts.assumeThreadsOversubscription,
opts.assumeNoThreadState,
opts.assumeNoNestedParallelism,
opts.openMPDeviceVersion, opts.noGPULib);
if (opts.isTargetDevice && !opts.hostIRFile.empty())
offloadMod.setHostIRFilePath(opts.hostIRFile);
auto strTriples = llvm::to_vector(
llvm::map_range(opts.targetTriples, [](const llvm::Triple &triple) {
return triple.normalize();
}));
offloadMod.setTargetTriples(strTriples);
}
}
void mlir::omp::setOpenMPVersionAttribute(ModuleOp module, int64_t version) {
module->setDiscardableAttr(
StringAttr::get(module.getContext(), llvm::Twine{"omp.version"}),
VersionAttr::get(module.getContext(), version));
}
void mlir::omp::setOpenMPIntegerWrapAround(ModuleOp module, bool value) {
module->setAttr(StringAttr::get(module.getContext(),
llvm::Twine{"omp.integer_wrap_around"}),
IntegerWrapAroundAttr::get(module.getContext(), value));
}
int64_t mlir::omp::getOpenMPVersionAttribute(ModuleOp module,
int64_t fallback) {
if (Attribute verAttr = module->getDiscardableAttr("omp.version"))
return llvm::cast<VersionAttr>(verAttr).getVersion();
return fallback;
}
bool mlir::omp::isOpenMPModule(ModuleOp module) {
return module->hasDiscardableAttr("omp.version");
}
static bool allocaUseRequiresSharedMem(const OpOperand &use) {
Operation *owner = use.getOwner();
if (auto parallelOp = dyn_cast<omp::ParallelOp>(owner)) {
if (llvm::is_contained(parallelOp.getReductionVars(), use.get()))
return true;
} else if (auto callOp = dyn_cast<CallOpInterface>(owner)) {
if (llvm::is_contained(callOp.getArgOperands(), use.get()))
return true;
}
// If it is used directly inside of a parallel region, it has to be replaced
// unless the use is a private clause.
if (owner->getParentOfType<omp::ParallelOp>()) {
if (auto argIface = dyn_cast<omp::BlockArgOpenMPOpInterface>(owner)) {
OperandRange privateVars = argIface.getPrivateVars();
auto it = llvm::find(privateVars, use.get());
if (it != privateVars.end()) {
ArrayAttr privateSyms = *argIface.getPrivateSyms();
size_t idx = std::distance(privateVars.begin(), it);
auto privateOp =
SymbolTable::lookupNearestSymbolFrom<omp::PrivateClauseOp>(
owner, cast<SymbolRefAttr>(privateSyms[idx]));
return privateOp.getDataSharingType() !=
omp::DataSharingClauseType::Private;
}
}
return true;
}
return false;
}
bool mlir::omp::allocaUsesRequireSharedMem(Value alloc) {
for (const OpOperand &use : alloc.getUses()) {
Operation *owner = use.getOwner();
if (isa<LLVM::AddrSpaceCastOp, LLVM::GEPOp>(owner)) {
if (llvm::any_of(owner->getResults(), [&](Value result) {
return allocaUsesRequireSharedMem(result);
}))
return true;
} else if (allocaUseRequiresSharedMem(use)) {
return true;
}
}
return false;
}
bool mlir::omp::opInSharedDeviceContext(Operation &op) {
if (isa<omp::ParallelOp>(op))
return false;
auto offloadIface = op.getParentOfType<omp::OffloadModuleInterface>();
if (!offloadIface || !offloadIface.getIsTargetDevice())
return false;
auto targetOp = op.getParentOfType<omp::TargetOp>();
// It must be inside of a generic omp.target or in a target device function,
// and not inside of omp.parallel.
if (auto parallelOp = op.getParentOfType<omp::ParallelOp>()) {
if (!targetOp || targetOp->isProperAncestor(parallelOp))
return false;
}
// The omp.target operation itself is considered in a shared device context in
// order to properly process its own allocation-defining entry block
// arguments.
if (!targetOp)
targetOp = dyn_cast<omp::TargetOp>(op);
if (targetOp) {
if (targetOp.getKernelType() != omp::TargetExecMode::generic)
return false;
} else {
auto declTargetIface = op.getParentOfType<omp::DeclareTargetInterface>();
if (!declTargetIface || !declTargetIface.isDeclareTarget() ||
declTargetIface.getDeclareTargetDeviceType() ==
omp::DeclareTargetDeviceType::host)
return false;
}
return true;
}