28#include "llvm/IR/LegacyPassManager.h"
29#include "llvm/Target/TargetMachine.h"
31#include "llvm/Bitcode/BitcodeWriter.h"
32#include "llvm/Config/Targets.h"
33#include "llvm/Support/FileSystem.h"
34#include "llvm/Support/FileUtilities.h"
35#include "llvm/Support/FormatVariadic.h"
36#include "llvm/Support/MemoryBuffer.h"
37#include "llvm/Support/Path.h"
38#include "llvm/Support/Process.h"
39#include "llvm/Support/Program.h"
40#include "llvm/Support/TargetSelect.h"
41#include "llvm/Support/raw_ostream.h"
51class XeVMTargetAttrImpl
52 :
public gpu::TargetAttrInterface::FallbackModel<XeVMTargetAttrImpl> {
54 std::optional<SmallVector<char, 0>>
55 serializeToObject(Attribute attribute, Operation *module,
56 const gpu::TargetOptions &
options)
const;
58 Attribute createObject(Attribute attribute, Operation *module,
59 const SmallVector<char, 0> &
object,
60 const gpu::TargetOptions &
options)
const;
67 XeVMTargetAttr::attachInterface<XeVMTargetAttrImpl>(*ctx);
82 xeTarget(xeTarget), librariesToLink(targetOptions.getLibrariesToLink()),
83 targetOptions(targetOptions) {
84 if (xeTarget.getLinkFiles())
85 librariesToLink.append(xeTarget.getLinkFiles().begin(),
86 xeTarget.getLinkFiles().end());
91std::optional<SmallVector<std::unique_ptr<llvm::Module>>>
99 return std::move(bcFiles);
110FailureOr<SmallVector<char, 0>>
112 StringRef inputFormat) {
113 using TmpFile = std::pair<llvm::SmallString<128>, llvm::FileRemover>;
115 std::optional<std::string> oclocCompiler =
findTool(
"ocloc");
119 std::string basename = llvm::formatv(
123 auto createTemp = [&](StringRef name,
124 StringRef suffix) -> FailureOr<TmpFile> {
126 if (
auto ec = llvm::sys::fs::createTemporaryFile(name, suffix, filePath))
128 <<
"Couldn't create the temp file: `" << filePath
129 <<
"`, error message: " << ec.message();
131 return TmpFile(filePath, llvm::FileRemover(filePath.c_str()));
134 FailureOr<TmpFile> asmFile = createTemp(basename,
"asm");
135 FailureOr<TmpFile> binFile = createTemp(basename,
"");
136 FailureOr<TmpFile> logFile = createTemp(basename,
"log");
137 if (failed(logFile) || failed(asmFile) || failed(binFile))
142 llvm::raw_fd_ostream asmStream(asmFile->first, ec);
144 return emitError(loc) <<
"Couldn't open the file: `" << asmFile->first
145 <<
"`, error message: " << ec.message();
148 if (asmStream.has_error())
150 <<
"An error occurred while writing the assembly to: `"
151 << asmFile->first <<
"`.";
156 std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>> cmdOpts =
159 const std::string cmdOptsStr =
"\"" + llvm::join(cmdOpts.second,
" ") +
"\"";
161 {
"ocloc",
"compile",
"-file", asmFile->first, inputFormat,
"-device",
162 getTarget().getChip(),
"-output", binFile->first,
"-output_no_suffix",
163 "-options", cmdOptsStr});
166#define DEBUG_TYPE "serialize-to-binary"
168 llvm::dbgs() <<
"Tool invocation for module: "
170 llvm::interleave(oclocArgs, llvm::dbgs(),
" ");
171 llvm::dbgs() <<
"\n";
178 if (message.empty()) {
179 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> toolStderr =
180 llvm::MemoryBuffer::getFile(logFile->first);
182 return emitError(loc) << toolName <<
" invocation failed. Log:\n"
183 << toolStderr->get()->getBuffer();
185 return emitError(loc) << toolName <<
" invocation failed.";
188 <<
" invocation failed, error message: " << message;
190 std::optional<StringRef> redirects[] = {
196 if (llvm::sys::ExecuteAndWait(oclocCompiler.value(), oclocArgs, std::nullopt,
197 redirects, 0, 0, &message))
198 return emitLogError(
"`ocloc`");
199 binFile->first.append(
".bin");
200 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> binaryBuffer =
201 llvm::MemoryBuffer::getFile(binFile->first);
203 return emitError(loc) <<
"Couldn't open the file: `" << binFile->first
204 <<
"`, error message: "
205 << binaryBuffer.getError().message();
207 StringRef bin = (*binaryBuffer)->getBuffer();
215 if (!pathRef.empty()) {
216 path.insert(path.begin(), pathRef.begin(), pathRef.end());
217 llvm::sys::path::append(path,
"bin", tool);
218 if (llvm::sys::fs::can_execute(path))
219 return StringRef(path.data(), path.size()).str();
222 if (std::optional<std::string> toolPath =
223 llvm::sys::Process::FindInEnvPath(
"PATH", tool))
227 <<
"Couldn't find the `" << tool
228 <<
"` binary. Please specify the toolkit "
229 "path via GpuModuleToBinaryPass or add the compiler to $PATH`.";
236 SPIRVSerializer(
Operation &module, XeVMTargetAttr xeTarget,
244 FailureOr<SmallVector<char, 0>>
245 moduleToObject(llvm::Module &llvmModule)
override;
250 std::optional<std::string>
251 translateToSPIRVBinary(llvm::Module &llvmModule,
252 llvm::TargetMachine &targetMachine);
256void SPIRVSerializer::init() {
257 static llvm::once_flag initializeBackendOnce;
258 llvm::call_once(initializeBackendOnce, []() {
259#if LLVM_HAS_SPIRV_TARGET
260 LLVMInitializeSPIRVTarget();
261 LLVMInitializeSPIRVTargetInfo();
262 LLVMInitializeSPIRVTargetMC();
263 LLVMInitializeSPIRVAsmPrinter();
268FailureOr<SmallVector<char, 0>>
269SPIRVSerializer::moduleToObject(llvm::Module &llvmModule) {
270#define DEBUG_TYPE "serialize-to-llvm"
272 llvm::dbgs() <<
"LLVM IR for module: " << getGPUModuleOp().getNameAttr()
274 llvm::dbgs() << llvmModule <<
"\n";
275 llvm::dbgs().flush();
280 if (targetOptions.getCompilationTarget() == gpu::CompilationTarget::Offload)
283#if !LLVM_HAS_SPIRV_TARGET
284 return getGPUModuleOp()->emitError(
285 "The `SPIRV` target was not built. Please enable "
286 "it when building LLVM.");
289 FailureOr<llvm::TargetMachine *> targetMachine = getOrCreateTargetMachine();
290 if (
failed(targetMachine))
291 return getGPUModuleOp().emitError()
292 <<
"Target Machine unavailable for triple " << triple
293 <<
", can't optimize with LLVM\n";
296 if (targetOptions.getCompilationTarget() ==
297 gpu::CompilationTarget::Assembly) {
298 FailureOr<SmallString<0>> serializedISA =
299 translateModuleToISA(llvmModule, **targetMachine,
300 [&]() {
return getGPUModuleOp().emitError(); });
301 if (
failed(serializedISA))
302 return getGPUModuleOp().emitError()
303 <<
"Failed translating the module to ISA." << triple
304 <<
", can't compile with LLVM\n";
306#define DEBUG_TYPE "serialize-to-isa"
308 llvm::dbgs() <<
"SPIR-V for module: " << getGPUModuleOp().getNameAttr()
310 llvm::dbgs() << *serializedISA <<
"\n";
311 llvm::dbgs().flush();
316 StringRef bin(serializedISA->c_str(), serializedISA->size() + 1);
317 return SmallVector<char, 0>(bin.begin(), bin.end());
325 std::optional<std::string> serializedSPIRVBinary =
326 translateToSPIRVBinary(llvmModule, **targetMachine);
327 if (!serializedSPIRVBinary)
328 return getGPUModuleOp().emitError()
329 <<
"Failed translating the module to Binary.";
331 if (serializedSPIRVBinary->size() % 4)
332 return getGPUModuleOp().emitError()
333 <<
"SPIRV code size must be a multiple of 4.";
335 StringRef bin(serializedSPIRVBinary->c_str(), serializedSPIRVBinary->size());
336 return SmallVector<char, 0>(bin.begin(), bin.end());
339std::optional<std::string>
340SPIRVSerializer::translateToSPIRVBinary(llvm::Module &llvmModule,
341 llvm::TargetMachine &targetMachine) {
342 std::string targetISA;
343 llvm::raw_string_ostream stream(targetISA);
346 llvm::buffer_ostream pstream(stream);
347 llvm::legacy::PassManager codegenPasses;
348 if (targetMachine.addPassesToEmitFile(codegenPasses, pstream,
nullptr,
349 llvm::CodeGenFileType::ObjectFile))
352 codegenPasses.run(llvmModule);
357std::optional<SmallVector<char, 0>>
358XeVMTargetAttrImpl::serializeToObject(Attribute attribute, Operation *module,
359 const gpu::TargetOptions &
options)
const {
362 auto gpuMod = dyn_cast<gpu::GPUModuleOp>(module);
364 module->emitError("expected to be a gpu.module op");
367 auto xeTarget = cast<XeVMTargetAttr>(attribute);
368 if (xeTarget.getTriple().starts_with(
"spirv")) {
369 gpuMod.walk([&](LLVM::LLVMFuncOp funcOp) {
370 if (funcOp->hasAttr(gpu::GPUDialect::getKernelFuncAttrName())) {
371 funcOp.setIntelReqdSubGroupSize(16);
377 SPIRVSerializer serializer(*module, cast<XeVMTargetAttr>(attribute),
381#if !LLVM_HAS_SPIRV_TARGET
382 module->emitError("Cannot run `TargetRegistry::lookupTarget()` for SPIRV "
383 "without having the target built.");
386 return serializer.run();
388 module->emitError("Unsupported XeVM target triple: ") << xeTarget.getTriple();
393XeVMTargetAttrImpl::createObject(Attribute attribute, Operation *module,
394 const SmallVector<char, 0> &
object,
395 const gpu::TargetOptions &
options)
const {
397 gpu::CompilationTarget format =
options.getCompilationTarget();
398 auto xeTarget = cast<XeVMTargetAttr>(attribute);
399 SmallVector<NamedAttribute, 2> properties;
400 if (format == gpu::CompilationTarget::Assembly)
401 properties.push_back(
402 builder.getNamedAttr(
"O", builder.getI32IntegerAttr(xeTarget.getO())));
404 DictionaryAttr objectProps;
405 if (!properties.empty())
406 objectProps = builder.getDictionaryAttr(properties);
408 return builder.getAttr<gpu::ObjectAttr>(
410 builder.getStringAttr(StringRef(
object.data(),
object.size())),
411 objectProps,
nullptr);
static llvm::ManagedStatic< PassManagerOptions > options
MLIRContext * getContext() const
Return the context this attribute belongs to.
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
bool addExtension(TypeID extensionID, std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
LogicalResult loadBitcodeFilesFromList(llvm::LLVMContext &context, ArrayRef< Attribute > librariesToLink, SmallVector< std::unique_ptr< llvm::Module > > &llvmModules, bool failureOnError=true)
Loads multiple bitcode files.
virtual FailureOr< SmallVector< char, 0 > > moduleToObject(llvm::Module &llvmModule)
Serializes the LLVM IR bitcode to an object file, by default it serializes to LLVM bitcode.
Operation & getOperation()
Returns the operation being serialized.
ModuleToObject(Operation &module, StringRef triple, StringRef chip, StringRef features={}, int optLevel=3, function_ref< void(llvm::Module &)> initialLlvmIRCallback={}, function_ref< void(llvm::Module &)> linkedLlvmIRCallback={}, function_ref< void(llvm::Module &)> optimizedLlvmIRCallback={}, function_ref< void(StringRef)> isaCallback={})
Operation & module
Module to transform to a binary object.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
void appendDialectRegistry(const DialectRegistry ®istry)
Append the contents of the given dialect registry to the registry associated with this context.
Operation is the basic unit of execution within MLIR.
static WalkResult advance()
static WalkResult interrupt()
This class serves as an opaque interface for passing options to the TargetAttrInterface methods.
Base class for all XeVM serializations from GPU modules into binary strings.
gpu::TargetOptions targetOptions
GPU compilation target options.
XeVMTargetAttr getTarget() const
Returns the target attribute.
FailureOr< SmallVector< char, 0 > > compileToBinary(StringRef asmStr, StringRef inputFormat)
Compiles to native code using ocloc.
std::optional< SmallVector< std::unique_ptr< llvm::Module > > > loadBitcodeFiles(llvm::Module &module) override
Loads the bitcode files in librariesToLink.
SmallVector< Attribute > librariesToLink
List of LLVM bitcode to link into after translation to LLVM IR.
SerializeGPUModuleBase(Operation &module, XeVMTargetAttr target, const gpu::TargetOptions &targetOptions={})
XeVMTargetAttr xeTarget
XeVM Target attribute.
std::optional< std::string > findTool(StringRef tool)
Returns the path to the tool used for serialization.
gpu::GPUModuleOp getGPUModuleOp()
Returns the gpu module being serialized.
void registerXeVMTargetInterfaceExternalModels(mlir::DialectRegistry ®istry)
Registers the TargetAttrInterface for the #xevm.target attribute in the given registry.
Include the generated interface declarations.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.