This change allows to expose through an interface attributes wrapping content as external resources, and the usage inside the ModuleToObject show how we will be able to provide runtime libraries without relying on the filesystem.
136 lines
5.0 KiB
C++
136 lines
5.0 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();
|
|
};
|
|
SmallVector<Attribute> librariesToLink;
|
|
for (const std::string &path : linkFiles)
|
|
librariesToLink.push_back(StringAttr::get(&getContext(), path));
|
|
TargetOptions targetOptions(toolkitPath, librariesToLink, cmdOptions,
|
|
elfSection, *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(op, *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 ®ion : 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();
|
|
}
|