llvm-project/mlir/lib/Dialect/GPU/Transforms/ModuleToBinary.cpp
Mehdi Amini d7da0ae4f4
[MLIR][NVVM] Reduce the scope of the LLVM_HAS_NVPTX_TARGET guard (#97349)
Most of the code here does not depend on the NVPTX target. In particular
the simple offload can just emit LLVM IR and we can use this without the
NVVM backend being built, which can be useful for a frontend that just
need to serialize the IR and leave it up to the runtime to JIT further.
2024-07-02 11:31:12 +02:00

133 lines
4.8 KiB
C++

//===- ModuleToBinary.cpp - Transforms GPU modules to GPU binaries ----------=//
//
// 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 the `GpuModuleToBinaryPass` pass, transforming GPU
// modules into GPU binaries.
//
//===----------------------------------------------------------------------===//
#include "mlir/Dialect/GPU/Transforms/Passes.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/GPU/IR/GPUDialect.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Dialect/LLVMIR/NVVMDialect.h"
#include "mlir/Dialect/LLVMIR/ROCDLDialect.h"
#include "mlir/Dialect/SPIRV/IR/SPIRVDialect.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSwitch.h"
using namespace mlir;
using namespace mlir::gpu;
namespace mlir {
#define GEN_PASS_DEF_GPUMODULETOBINARYPASS
#include "mlir/Dialect/GPU/Transforms/Passes.h.inc"
} // namespace mlir
namespace {
class GpuModuleToBinaryPass
: public impl::GpuModuleToBinaryPassBase<GpuModuleToBinaryPass> {
public:
using Base::Base;
void runOnOperation() final;
};
} // namespace
void GpuModuleToBinaryPass::runOnOperation() {
RewritePatternSet patterns(&getContext());
auto targetFormat =
llvm::StringSwitch<std::optional<CompilationTarget>>(compilationTarget)
.Cases("offloading", "llvm", CompilationTarget::Offload)
.Cases("assembly", "isa", CompilationTarget::Assembly)
.Cases("binary", "bin", CompilationTarget::Binary)
.Cases("fatbinary", "fatbin", CompilationTarget::Fatbin)
.Default(std::nullopt);
if (!targetFormat)
getOperation()->emitError() << "Invalid format specified.";
// Lazy symbol table builder callback.
std::optional<SymbolTable> parentTable;
auto lazyTableBuilder = [&]() -> SymbolTable * {
// Build the table if it has not been built.
if (!parentTable) {
Operation *table = SymbolTable::getNearestSymbolTable(getOperation());
// It's up to the target attribute to determine if failing to find a
// symbol table is an error.
if (!table)
return nullptr;
parentTable = SymbolTable(table);
}
return &parentTable.value();
};
TargetOptions targetOptions(toolkitPath, linkFiles, cmdOptions, *targetFormat,
lazyTableBuilder);
if (failed(transformGpuModulesToBinaries(
getOperation(), OffloadingLLVMTranslationAttrInterface(nullptr),
targetOptions)))
return signalPassFailure();
}
namespace {
LogicalResult moduleSerializer(GPUModuleOp op,
OffloadingLLVMTranslationAttrInterface handler,
const TargetOptions &targetOptions) {
OpBuilder builder(op->getContext());
SmallVector<Attribute> objects;
// Fail if there are no target attributes
if (!op.getTargetsAttr())
return op.emitError("the module has no target attributes");
// Serialize all targets.
for (auto targetAttr : op.getTargetsAttr()) {
assert(targetAttr && "Target attribute cannot be null.");
auto target = dyn_cast<gpu::TargetAttrInterface>(targetAttr);
assert(target &&
"Target attribute doesn't implements `TargetAttrInterface`.");
std::optional<SmallVector<char, 0>> serializedModule =
target.serializeToObject(op, targetOptions);
if (!serializedModule) {
op.emitError("An error happened while serializing the module.");
return failure();
}
Attribute object = target.createObject(*serializedModule, targetOptions);
if (!object) {
op.emitError("An error happened while creating the object.");
return failure();
}
objects.push_back(object);
}
if (auto moduleHandler =
dyn_cast_or_null<OffloadingLLVMTranslationAttrInterface>(
op.getOffloadingHandlerAttr());
!handler && moduleHandler)
handler = moduleHandler;
builder.setInsertionPointAfter(op);
builder.create<gpu::BinaryOp>(op.getLoc(), op.getName(), handler,
builder.getArrayAttr(objects));
op->erase();
return success();
}
} // namespace
LogicalResult mlir::gpu::transformGpuModulesToBinaries(
Operation *op, OffloadingLLVMTranslationAttrInterface handler,
const gpu::TargetOptions &targetOptions) {
for (Region &region : op->getRegions())
for (Block &block : region.getBlocks())
for (auto module :
llvm::make_early_inc_range(block.getOps<GPUModuleOp>()))
if (failed(moduleSerializer(module, handler, targetOptions)))
return failure();
return success();
}