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);
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;
248 std::optional<SmallVector<char, 0>> run()
override;
253 std::optional<std::string>
254 translateToSPIRVBinary(llvm::Module &llvmModule,
255 llvm::TargetMachine &targetMachine);
259void SPIRVSerializer::init() {
260 static llvm::once_flag initializeBackendOnce;
261 llvm::call_once(initializeBackendOnce, []() {
262#if LLVM_HAS_SPIRV_TARGET
263 LLVMInitializeSPIRVTarget();
264 LLVMInitializeSPIRVTargetInfo();
265 LLVMInitializeSPIRVTargetMC();
266 LLVMInitializeSPIRVAsmPrinter();
271#if LLVM_HAS_SPIRV_TARGET
272static const std::vector<std::string> getDefaultSPIRVExtensions() {
274 "SPV_EXT_relaxed_printf_string_address_space",
275 "SPV_INTEL_cache_controls",
276 "SPV_INTEL_variable_length_array",
284SPIRVTranslateModule(Module *M, std::string &SpirvObj, std::string &ErrMsg,
285 const std::vector<std::string> &AllowExtNames,
286 const std::vector<std::string> &Opts);
290FailureOr<SmallVector<char, 0>>
291SPIRVSerializer::moduleToObject(llvm::Module &llvmModule) {
292#define DEBUG_TYPE "serialize-to-llvm"
294 llvm::dbgs() <<
"LLVM IR for module: " << getGPUModuleOp().getNameAttr()
296 llvm::dbgs() << llvmModule <<
"\n";
297 llvm::dbgs().flush();
302 if (targetOptions.getCompilationTarget() == gpu::CompilationTarget::Offload)
305#if !LLVM_HAS_SPIRV_TARGET
306 return getGPUModuleOp()->emitError(
307 "The `SPIRV` target was not built. Please enable "
308 "it when building LLVM.");
314 if (targetOptions.getCompilationTarget() ==
315 gpu::CompilationTarget::Assembly) {
316 FailureOr<llvm::TargetMachine *> targetMachine = getOrCreateTargetMachine();
317 if (
failed(targetMachine))
318 return getGPUModuleOp().emitError()
319 <<
"Target Machine unavailable for triple " << triple
320 <<
", can't optimize with LLVM\n";
322 FailureOr<SmallString<0>> serializedISA =
323 translateModuleToISA(llvmModule, **targetMachine,
324 [&]() {
return getGPUModuleOp().emitError(); });
325 if (
failed(serializedISA))
326 return getGPUModuleOp().emitError()
327 <<
"Failed translating the module to ISA." << triple
328 <<
", can't compile with LLVM\n";
330#define DEBUG_TYPE "serialize-to-isa"
332 llvm::dbgs() <<
"SPIR-V for module: " << getGPUModuleOp().getNameAttr()
334 llvm::dbgs() << *serializedISA <<
"\n";
335 llvm::dbgs().flush();
340 StringRef bin(serializedISA->c_str(), serializedISA->size() + 1);
341 return SmallVector<char, 0>(bin.begin(), bin.end());
345 std::string serializedSPIRVBinary;
347 std::vector<std::string> Opts;
348 Opts.push_back(triple.str());
349 Opts.push_back(std::to_string(optLevel));
352 SPIRVTranslateModule(&llvmModule, serializedSPIRVBinary, ErrMsg,
353 getDefaultSPIRVExtensions(), Opts);
356 return getGPUModuleOp().emitError()
357 <<
"Failed translating the module to Binary."
358 <<
"Error message: " << ErrMsg;
360 if (serializedSPIRVBinary.size() % 4)
361 return getGPUModuleOp().emitError()
362 <<
"SPIRV code size must be a multiple of 4.";
364 StringRef bin(serializedSPIRVBinary.c_str(), serializedSPIRVBinary.size());
365 return SmallVector<char, 0>(bin.begin(), bin.end());
369std::optional<SmallVector<char, 0>> SPIRVSerializer::run() {
371 llvm::LLVMContext llvmContext;
372 std::unique_ptr<llvm::Module> llvmModule = translateToLLVMIR(llvmContext);
374 getOperation().emitError() <<
"Failed creating the llvm::Module.";
377 setDataLayoutAndTriple(*llvmModule);
379 if (initialLlvmIRCallback)
380 initialLlvmIRCallback(*llvmModule);
383 handleModulePreLink(*llvmModule);
385 auto libs = loadBitcodeFiles(*llvmModule);
389 if (
failed(linkFiles(*llvmModule, std::move(*libs))))
391 handleModulePostLink(*llvmModule);
394 if (linkedLlvmIRCallback)
395 linkedLlvmIRCallback(*llvmModule);
398 return moduleToObject(*llvmModule);
401std::optional<std::string>
402SPIRVSerializer::translateToSPIRVBinary(llvm::Module &llvmModule,
403 llvm::TargetMachine &targetMachine) {
404 std::string targetISA;
405 llvm::raw_string_ostream stream(targetISA);
408 llvm::buffer_ostream pstream(stream);
409 llvm::legacy::PassManager codegenPasses;
410 if (targetMachine.addPassesToEmitFile(codegenPasses, pstream,
nullptr,
411 llvm::CodeGenFileType::ObjectFile))
414 codegenPasses.run(llvmModule);
419std::optional<mlir::gpu::SerializedObject>
420XeVMTargetAttrImpl::serializeToObject(Attribute attribute, Operation *module,
421 const gpu::TargetOptions &
options)
const {
424 auto gpuMod = dyn_cast<gpu::GPUModuleOp>(module);
426 module->emitError("expected to be a gpu.module op");
429 auto xeTarget = cast<XeVMTargetAttr>(attribute);
430 if (xeTarget.getTriple().starts_with(
"spirv")) {
431 gpuMod.walk([&](LLVM::LLVMFuncOp funcOp) {
432 if (funcOp->hasAttr(gpu::GPUDialect::getKernelFuncAttrName())) {
433 funcOp.setIntelReqdSubGroupSize(16);
439 SPIRVSerializer serializer(*module, cast<XeVMTargetAttr>(attribute),
443#if !LLVM_HAS_SPIRV_TARGET
444 module->emitError("Cannot run `TargetRegistry::lookupTarget()` for SPIRV "
445 "without having the target built.");
448 std::optional<SmallVector<char, 0>> binary = serializer.run();
451 return gpu::SerializedObject{std::move(*binary)};
453 module->emitError("Unsupported XeVM target triple: ") << xeTarget.getTriple();
458XeVMTargetAttrImpl::createObject(Attribute attribute, Operation *module,
459 const mlir::gpu::SerializedObject &
object,
460 const gpu::TargetOptions &
options)
const {
462 gpu::CompilationTarget format =
options.getCompilationTarget();
463 auto xeTarget = cast<XeVMTargetAttr>(attribute);
464 SmallVector<NamedAttribute, 2> properties;
465 if (format == gpu::CompilationTarget::Assembly)
466 properties.push_back(
467 builder.getNamedAttr(
"O", builder.getI32IntegerAttr(xeTarget.getO())));
469 DictionaryAttr objectProps;
470 if (!properties.empty())
471 objectProps = builder.getDictionaryAttr(properties);
473 return builder.getAttr<gpu::ObjectAttr>(
475 builder.getStringAttr(
476 StringRef(
object.getObject().data(),
object.getObject().size())),
477 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.