Provide default for gpuBinaryAnnotation so that we don't need to specify it in tests. The annotation likely only needs to be target specific if we want to lower to e.g. both CUDA and ROCDL. Reviewed By: herhut, bondhugula Differential Revision: https://reviews.llvm.org/D98168
178 lines
6.7 KiB
C++
178 lines
6.7 KiB
C++
//===- ConvertKernelFuncToBlob.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 GPU. Currently
|
|
// only translates the function itself but no dependencies.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "mlir/Conversion/GPUCommon/GPUCommonPass.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/BuiltinOps.h"
|
|
#include "mlir/Pass/Pass.h"
|
|
#include "mlir/Pass/PassRegistry.h"
|
|
#include "mlir/Support/LogicalResult.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 {
|
|
|
|
/// A pass converting tagged kernel modules to a blob with target instructions.
|
|
///
|
|
/// If tagged as a kernel module, each contained function is translated to
|
|
/// user-specified IR. A user provided BlobGenerator then compiles the IR to
|
|
/// GPU binary code, which is then attached as an attribute to the function.
|
|
/// The function body is erased.
|
|
class GpuKernelToBlobPass
|
|
: public PassWrapper<GpuKernelToBlobPass, OperationPass<gpu::GPUModuleOp>> {
|
|
public:
|
|
GpuKernelToBlobPass(LoweringCallback loweringCallback,
|
|
BlobGenerator blobGenerator, StringRef triple,
|
|
StringRef targetChip, StringRef features,
|
|
StringRef gpuBinaryAnnotation)
|
|
: loweringCallback(loweringCallback), blobGenerator(blobGenerator),
|
|
triple(triple), targetChip(targetChip), features(features) {
|
|
if (!gpuBinaryAnnotation.empty())
|
|
this->gpuBinaryAnnotation = gpuBinaryAnnotation.str();
|
|
}
|
|
|
|
GpuKernelToBlobPass(const GpuKernelToBlobPass &other)
|
|
: loweringCallback(other.loweringCallback),
|
|
blobGenerator(other.blobGenerator), triple(other.triple),
|
|
targetChip(other.targetChip), features(other.features) {}
|
|
|
|
void runOnOperation() override {
|
|
gpu::GPUModuleOp module = getOperation();
|
|
|
|
// Lower the module to an LLVM IR module using a separate context to enable
|
|
// multi-threaded processing.
|
|
llvm::LLVMContext llvmContext;
|
|
std::unique_ptr<llvm::Module> llvmModule =
|
|
loweringCallback(module, llvmContext, "LLVMDialectModule");
|
|
if (!llvmModule)
|
|
return signalPassFailure();
|
|
|
|
// Translate the llvm module to a target blob and attach the result as
|
|
// attribute to the module.
|
|
if (auto blobAttr = translateGPUModuleToBinaryAnnotation(
|
|
*llvmModule, module.getLoc(), module.getName()))
|
|
module->setAttr(gpuBinaryAnnotation, blobAttr);
|
|
else
|
|
signalPassFailure();
|
|
}
|
|
|
|
private:
|
|
std::string translateModuleToISA(llvm::Module &module,
|
|
llvm::TargetMachine &targetMachine);
|
|
|
|
/// Converts llvmModule to a blob with target instructions using the
|
|
/// user-provided generator. Location is used for error reporting and name is
|
|
/// forwarded to the blob generator to use in its logging mechanisms.
|
|
OwnedBlob convertModuleToBlob(llvm::Module &llvmModule, Location loc,
|
|
StringRef name);
|
|
|
|
/// Translates llvmModule to a blob with target instructions and returns the
|
|
/// result as attribute.
|
|
StringAttr translateGPUModuleToBinaryAnnotation(llvm::Module &llvmModule,
|
|
Location loc, StringRef name);
|
|
|
|
LoweringCallback loweringCallback;
|
|
BlobGenerator blobGenerator;
|
|
|
|
llvm::Triple triple;
|
|
std::string targetChip;
|
|
std::string features;
|
|
|
|
Option<std::string> gpuBinaryAnnotation{
|
|
*this, "gpu-binary-annotation",
|
|
llvm::cl::desc("Annotation attribute string for GPU binary"),
|
|
llvm::cl::init(gpu::getDefaultGpuBinaryAnnotation())};
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
std::string gpu::getDefaultGpuBinaryAnnotation() { return "gpu.binary"; }
|
|
|
|
std::string
|
|
GpuKernelToBlobPass::translateModuleToISA(llvm::Module &module,
|
|
llvm::TargetMachine &targetMachine) {
|
|
std::string targetISA;
|
|
{
|
|
llvm::raw_string_ostream stream(targetISA);
|
|
llvm::buffer_ostream pstream(stream);
|
|
llvm::legacy::PassManager codegenPasses;
|
|
targetMachine.addPassesToEmitFile(codegenPasses, pstream, nullptr,
|
|
llvm::CGFT_AssemblyFile);
|
|
codegenPasses.run(module);
|
|
}
|
|
|
|
return targetISA;
|
|
}
|
|
|
|
OwnedBlob GpuKernelToBlobPass::convertModuleToBlob(llvm::Module &llvmModule,
|
|
Location loc,
|
|
StringRef name) {
|
|
std::unique_ptr<llvm::TargetMachine> targetMachine;
|
|
{
|
|
std::string error;
|
|
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(), targetChip,
|
|
features, {}, {}));
|
|
if (targetMachine == nullptr) {
|
|
emitError(loc, "cannot initialize target machine");
|
|
return {};
|
|
}
|
|
}
|
|
|
|
llvmModule.setDataLayout(targetMachine->createDataLayout());
|
|
|
|
auto targetISA = translateModuleToISA(llvmModule, *targetMachine);
|
|
|
|
return blobGenerator(targetISA, loc, name);
|
|
}
|
|
|
|
StringAttr GpuKernelToBlobPass::translateGPUModuleToBinaryAnnotation(
|
|
llvm::Module &llvmModule, Location loc, StringRef name) {
|
|
auto blob = convertModuleToBlob(llvmModule, loc, name);
|
|
if (!blob)
|
|
return {};
|
|
return StringAttr::get(loc->getContext(), {blob->data(), blob->size()});
|
|
}
|
|
|
|
std::unique_ptr<OperationPass<gpu::GPUModuleOp>>
|
|
mlir::createConvertGPUKernelToBlobPass(LoweringCallback loweringCallback,
|
|
BlobGenerator blobGenerator,
|
|
StringRef triple, StringRef targetChip,
|
|
StringRef features,
|
|
StringRef gpuBinaryAnnotation) {
|
|
return std::make_unique<GpuKernelToBlobPass>(loweringCallback, blobGenerator,
|
|
triple, targetChip, features,
|
|
gpuBinaryAnnotation);
|
|
}
|