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<mlir::gpu::SerializedObject>
55 serializeToObject(Attribute attribute, Operation *module,
56 const gpu::TargetOptions &
options)
const;
58 Attribute createObject(Attribute attribute, Operation *module,
59 const mlir::gpu::SerializedObject &
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);
111 StringRef asmStr, StringRef inputFormat =
"-spirv_input") {
112 using TmpFile = std::pair<llvm::SmallString<128>, llvm::FileRemover>;
114 std::optional<std::string> oclocCompiler =
findTool(
"ocloc");
118 std::string basename = llvm::formatv(
122 auto createTemp = [&](StringRef name,
123 StringRef suffix) -> FailureOr<TmpFile> {
125 if (
auto ec = llvm::sys::fs::createTemporaryFile(name, suffix, filePath))
127 <<
"Couldn't create the temp file: `" << filePath
128 <<
"`, error message: " << ec.message();
130 return TmpFile(filePath, llvm::FileRemover(filePath.c_str()));
133 FailureOr<TmpFile> asmFile = createTemp(basename,
"asm");
134 FailureOr<TmpFile> binFile = createTemp(basename,
"");
135 FailureOr<TmpFile> logFile = createTemp(basename,
"log");
136 if (failed(logFile) || failed(asmFile) || failed(binFile))
141 llvm::raw_fd_ostream asmStream(asmFile->first, ec);
143 return emitError(loc) <<
"Couldn't open the file: `" << asmFile->first
144 <<
"`, error message: " << ec.message();
147 if (asmStream.has_error())
149 <<
"An error occurred while writing the assembly to: `"
150 << asmFile->first <<
"`.";
155 std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>> cmdOpts =
158 const std::string cmdOptsStr =
"\"" + llvm::join(cmdOpts.second,
" ") +
"\"";
160 {
"ocloc",
"compile",
"-file", asmFile->first, inputFormat,
"-device",
161 getTarget().getChip(),
"-output", binFile->first,
"-output_no_suffix",
162 "-options", cmdOptsStr});
165#define DEBUG_TYPE "serialize-to-binary"
167 llvm::dbgs() <<
"Tool invocation for module: "
169 llvm::interleave(oclocArgs, llvm::dbgs(),
" ");
170 llvm::dbgs() <<
"\n";
177 if (message.empty()) {
178 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> toolStderr =
179 llvm::MemoryBuffer::getFile(logFile->first);
181 return emitError(loc) << toolName <<
" invocation failed. Log:\n"
182 << toolStderr->get()->getBuffer();
184 return emitError(loc) << toolName <<
" invocation failed.";
187 <<
" invocation failed, error message: " << message;
189 std::optional<StringRef> redirects[] = {
195 if (llvm::sys::ExecuteAndWait(oclocCompiler.value(), oclocArgs, std::nullopt,
196 redirects, 0, 0, &message))
197 return emitLogError(
"`ocloc`");
198 binFile->first.append(
".bin");
199 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> binaryBuffer =
200 llvm::MemoryBuffer::getFile(binFile->first);
202 return emitError(loc) <<
"Couldn't open the file: `" << binFile->first
203 <<
"`, error message: "
204 << binaryBuffer.getError().message();
206 StringRef bin = (*binaryBuffer)->getBuffer();
214 if (!pathRef.empty()) {
215 path.insert(path.begin(), pathRef.begin(), pathRef.end());
216 llvm::sys::path::append(path,
"bin", tool);
217 if (llvm::sys::fs::can_execute(path))
218 return StringRef(path.data(), path.size()).str();
221 if (std::optional<std::string> toolPath =
222 llvm::sys::Process::FindInEnvPath(
"PATH", tool))
226 <<
"Couldn't find the `" << tool
227 <<
"` binary. Please specify the toolkit "
228 "path via GpuModuleToBinaryPass or add the compiler to $PATH`.";
235 SPIRVSerializer(
Operation &module, XeVMTargetAttr xeTarget,
243 FailureOr<SmallVector<char, 0>>
244 moduleToObject(llvm::Module &llvmModule)
override;
247 std::optional<SmallVector<char, 0>> run()
override;
252 std::optional<std::string>
253 translateToSPIRVBinary(llvm::Module &llvmModule,
254 llvm::TargetMachine &targetMachine);
258void SPIRVSerializer::init() {
259 static llvm::once_flag initializeBackendOnce;
260 llvm::call_once(initializeBackendOnce, []() {
261#if LLVM_HAS_SPIRV_TARGET
262 LLVMInitializeSPIRVTarget();
263 LLVMInitializeSPIRVTargetInfo();
264 LLVMInitializeSPIRVTargetMC();
265 LLVMInitializeSPIRVAsmPrinter();
270#if LLVM_HAS_SPIRV_TARGET
271static const std::vector<std::string> getDefaultSPIRVExtensions() {
273 "SPV_EXT_relaxed_printf_string_address_space",
274 "SPV_INTEL_cache_controls",
275 "SPV_INTEL_variable_length_array",
283SPIRVTranslateModule(Module *M, std::string &SpirvObj, std::string &ErrMsg,
284 const std::vector<std::string> &AllowExtNames,
285 const std::vector<std::string> &Opts);
289FailureOr<SmallVector<char, 0>>
290SPIRVSerializer::moduleToObject(llvm::Module &llvmModule) {
291#define DEBUG_TYPE "serialize-to-llvm"
293 llvm::dbgs() <<
"LLVM IR for module: " << getGPUModuleOp().getNameAttr()
295 llvm::dbgs() << llvmModule <<
"\n";
296 llvm::dbgs().flush();
301 if (targetOptions.getCompilationTarget() == gpu::CompilationTarget::Offload)
304#if !LLVM_HAS_SPIRV_TARGET
305 return getGPUModuleOp()->emitError(
306 "The `SPIRV` target was not built. Please enable "
307 "it when building LLVM.");
313 if (targetOptions.getCompilationTarget() ==
314 gpu::CompilationTarget::Assembly) {
315 FailureOr<llvm::TargetMachine *> targetMachine = getOrCreateTargetMachine();
316 if (
failed(targetMachine))
317 return getGPUModuleOp().emitError()
318 <<
"Target Machine unavailable for triple " << triple
319 <<
", can't optimize with LLVM\n";
321 FailureOr<SmallString<0>> serializedISA =
322 translateModuleToISA(llvmModule, **targetMachine,
323 [&]() {
return getGPUModuleOp().emitError(); });
324 if (
failed(serializedISA))
325 return getGPUModuleOp().emitError()
326 <<
"Failed translating the module to ISA." << triple
327 <<
", can't compile with LLVM\n";
329#define DEBUG_TYPE "serialize-to-isa"
331 llvm::dbgs() <<
"SPIR-V for module: " << getGPUModuleOp().getNameAttr()
333 llvm::dbgs() << *serializedISA <<
"\n";
334 llvm::dbgs().flush();
339 StringRef bin(serializedISA->c_str(), serializedISA->size() + 1);
340 return SmallVector<char, 0>(bin.begin(), bin.end());
357 std::string serializedSPIRVBinary;
359 std::vector<std::string> Opts;
360 Opts.push_back(triple.str());
361 Opts.push_back(std::to_string(optLevel));
364 SPIRVTranslateModule(&llvmModule, serializedSPIRVBinary, ErrMsg,
365 getDefaultSPIRVExtensions(), Opts);
368 return getGPUModuleOp().emitError()
369 <<
"Failed translating the module to Binary."
370 <<
"Error message: " << ErrMsg;
372 if (serializedSPIRVBinary.size() % 4)
373 return getGPUModuleOp().emitError()
374 <<
"SPIRV code size must be a multiple of 4.";
376 StringRef spirvBin(serializedSPIRVBinary.c_str(),
377 serializedSPIRVBinary.size());
378 return compileToBinary(spirvBin,
"-spirv_input");
382std::optional<SmallVector<char, 0>> SPIRVSerializer::run() {
384 llvm::LLVMContext llvmContext;
385 std::unique_ptr<llvm::Module> llvmModule = translateToLLVMIR(llvmContext);
387 getOperation().emitError() <<
"Failed creating the llvm::Module.";
390 setDataLayoutAndTriple(*llvmModule);
392 if (initialLlvmIRCallback)
393 initialLlvmIRCallback(*llvmModule);
396 handleModulePreLink(*llvmModule);
398 auto libs = loadBitcodeFiles(*llvmModule);
402 if (
failed(linkFiles(*llvmModule, std::move(*libs))))
404 handleModulePostLink(*llvmModule);
407 if (linkedLlvmIRCallback)
408 linkedLlvmIRCallback(*llvmModule);
411 return moduleToObject(*llvmModule);
414std::optional<std::string>
415SPIRVSerializer::translateToSPIRVBinary(llvm::Module &llvmModule,
416 llvm::TargetMachine &targetMachine) {
417 std::string targetISA;
418 llvm::raw_string_ostream stream(targetISA);
421 llvm::buffer_ostream pstream(stream);
422 llvm::legacy::PassManager codegenPasses;
423 if (targetMachine.addPassesToEmitFile(codegenPasses, pstream,
nullptr,
424 llvm::CodeGenFileType::ObjectFile))
427 codegenPasses.run(llvmModule);
432std::optional<mlir::gpu::SerializedObject>
433XeVMTargetAttrImpl::serializeToObject(Attribute attribute, Operation *module,
434 const gpu::TargetOptions &
options)
const {
437 auto gpuMod = dyn_cast<gpu::GPUModuleOp>(module);
439 module->emitError("expected to be a gpu.module op");
442 auto xeTarget = cast<XeVMTargetAttr>(attribute);
443 if (xeTarget.getTriple().starts_with(
"spirv")) {
444 gpuMod.walk([&](LLVM::LLVMFuncOp funcOp) {
445 if (funcOp->hasAttr(gpu::GPUDialect::getKernelFuncAttrName())) {
446 funcOp.setIntelReqdSubGroupSize(16);
452 SPIRVSerializer serializer(*module, cast<XeVMTargetAttr>(attribute),
456#if !LLVM_HAS_SPIRV_TARGET
457 module->emitError("Cannot run `TargetRegistry::lookupTarget()` for SPIRV "
458 "without having the target built.");
461 std::optional<SmallVector<char, 0>> binary = serializer.run();
464 return gpu::SerializedObject{std::move(*binary)};
466 module->emitError("Unsupported XeVM target triple: ") << xeTarget.getTriple();
471XeVMTargetAttrImpl::createObject(Attribute attribute, Operation *module,
472 const mlir::gpu::SerializedObject &
object,
473 const gpu::TargetOptions &
options)
const {
475 gpu::CompilationTarget format =
options.getCompilationTarget();
476 auto xeTarget = cast<XeVMTargetAttr>(attribute);
477 SmallVector<NamedAttribute, 2> properties;
478 if (format == gpu::CompilationTarget::Assembly)
479 properties.push_back(
480 builder.getNamedAttr(
"O", builder.getI32IntegerAttr(xeTarget.getO())));
482 DictionaryAttr objectProps;
483 if (!properties.empty())
484 objectProps = builder.getDictionaryAttr(properties);
486 return builder.getAttr<gpu::ObjectAttr>(
488 builder.getStringAttr(
489 StringRef(
object.getObject().data(),
object.getObject().size())),
490 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.