Files
clang-p2996/mlir/lib/Conversion/GPUToCUDA/ConvertKernelFuncToCubin.cpp
Stephan Herhut c22876b550 [MLIR] Add extra locking during cubin generation.
We also need to lock the LLVMDialect mutex when initializing
LLVM targets or destroying llvm modules concurrently. Added another
scoped lock to that effect.

Differential Revision: https://reviews.llvm.org/D78580
2020-04-22 08:57:45 +02:00

166 lines
5.9 KiB
C++

//===- ConvertKernelFuncToCubin.cpp - MLIR GPU lowering passes ------------===//
//
// 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 pass to convert gpu kernel functions into a
// corresponding binary blob that can be executed on a CUDA GPU. Currently
// only translates the function itself but no dependencies.
//
//===----------------------------------------------------------------------===//
#include "mlir/Conversion/GPUToCUDA/GPUToCUDAPass.h"
#include "mlir/Dialect/GPU/GPUDialect.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/Function.h"
#include "mlir/IR/Module.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Pass/PassRegistry.h"
#include "mlir/Support/LogicalResult.h"
#include "mlir/Target/NVVMIR.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/Twine.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
using namespace mlir;
namespace {
// TODO(herhut): Move to shared location.
static constexpr const char *kCubinAnnotation = "nvvm.cubin";
/// A pass converting tagged kernel modules to cubin blobs.
///
/// If tagged as a kernel module, each contained function is translated to NVVM
/// IR and further to PTX. A user provided CubinGenerator compiles the PTX to
/// GPU binary code, which is then attached as an attribute to the function. The
/// function body is erased.
class GpuKernelToCubinPass
: public PassWrapper<GpuKernelToCubinPass,
OperationPass<gpu::GPUModuleOp>> {
public:
GpuKernelToCubinPass(CubinGenerator cubinGenerator)
: cubinGenerator(cubinGenerator) {}
void runOnOperation() override {
gpu::GPUModuleOp module = getOperation();
// Lock access to the llvm context.
llvm::sys::SmartScopedLock<true> scopedLock(
module.getContext()
->getRegisteredDialect<LLVM::LLVMDialect>()
->getLLVMContextMutex());
// Make sure the NVPTX target is initialized.
LLVMInitializeNVPTXTarget();
LLVMInitializeNVPTXTargetInfo();
LLVMInitializeNVPTXTargetMC();
LLVMInitializeNVPTXAsmPrinter();
auto llvmModule = translateModuleToNVVMIR(module);
if (!llvmModule)
return signalPassFailure();
// Translate the module to CUBIN and attach the result as attribute to the
// module.
if (auto cubinAttr = translateGPUModuleToCubinAnnotation(
*llvmModule, module.getLoc(), module.getName()))
module.setAttr(kCubinAnnotation, cubinAttr);
else
signalPassFailure();
}
private:
std::string translateModuleToPtx(llvm::Module &module,
llvm::TargetMachine &target_machine);
/// Converts llvmModule to cubin using the user-provided generator. Location
/// is used for error reporting and name is forwarded to the CUBIN generator
/// to use in its logging mechanisms.
OwnedCubin convertModuleToCubin(llvm::Module &llvmModule, Location loc,
StringRef name);
/// Translates llvmModule to cubin and returns the result as attribute.
StringAttr translateGPUModuleToCubinAnnotation(llvm::Module &llvmModule,
Location loc, StringRef name);
CubinGenerator cubinGenerator;
};
} // anonymous namespace
std::string GpuKernelToCubinPass::translateModuleToPtx(
llvm::Module &module, llvm::TargetMachine &target_machine) {
std::string ptx;
{
// Clone the llvm module into a new context to enable concurrent compilation
// with multiple threads.
// TODO(zinenko): Reevaluate model of ownership of LLVMContext in
// LLVMDialect.
llvm::LLVMContext llvmContext;
auto clone = LLVM::cloneModuleIntoNewContext(&llvmContext, &module);
llvm::raw_string_ostream stream(ptx);
llvm::buffer_ostream pstream(stream);
llvm::legacy::PassManager codegen_passes;
target_machine.addPassesToEmitFile(codegen_passes, pstream, nullptr,
llvm::CGFT_AssemblyFile);
codegen_passes.run(*clone);
}
return ptx;
}
OwnedCubin GpuKernelToCubinPass::convertModuleToCubin(llvm::Module &llvmModule,
Location loc,
StringRef name) {
std::unique_ptr<llvm::TargetMachine> targetMachine;
{
std::string error;
// TODO(herhut): Make triple configurable.
constexpr const char *cudaTriple = "nvptx64-nvidia-cuda";
llvm::Triple triple(cudaTriple);
const llvm::Target *target =
llvm::TargetRegistry::lookupTarget("", triple, error);
if (target == nullptr) {
emitError(loc, "cannot initialize target triple");
return {};
}
targetMachine.reset(
target->createTargetMachine(triple.str(), "sm_35", "+ptx60", {}, {}));
}
// Set the data layout of the llvm module to match what the ptx target needs.
llvmModule.setDataLayout(targetMachine->createDataLayout());
auto ptx = translateModuleToPtx(llvmModule, *targetMachine);
return cubinGenerator(ptx, loc, name);
}
StringAttr GpuKernelToCubinPass::translateGPUModuleToCubinAnnotation(
llvm::Module &llvmModule, Location loc, StringRef name) {
auto cubin = convertModuleToCubin(llvmModule, loc, name);
if (!cubin)
return {};
return StringAttr::get({cubin->data(), cubin->size()}, loc->getContext());
}
std::unique_ptr<OperationPass<gpu::GPUModuleOp>>
mlir::createConvertGPUKernelToCubinPass(CubinGenerator cubinGenerator) {
return std::make_unique<GpuKernelToCubinPass>(cubinGenerator);
}