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"
51 class XeVMTargetAttrImpl
52 :
public gpu::TargetAttrInterface::FallbackModel<XeVMTargetAttrImpl> {
54 std::optional<SmallVector<char, 0>>
67 XeVMTargetAttr::attachInterface<XeVMTargetAttrImpl>(*ctx);
79 Operation &module, XeVMTargetAttr xeTarget,
81 : ModuleToObject(module, xeTarget.getTriple(),
"", {}, xeTarget.getO()),
82 xeTarget(xeTarget), librariesToLink(targetOptions.getLibrariesToLink()),
83 targetOptions(targetOptions) {
84 if (xeTarget.getLinkFiles())
85 librariesToLink.append(xeTarget.getLinkFiles().begin(),
86 xeTarget.getLinkFiles().end());
91 std::optional<SmallVector<std::unique_ptr<llvm::Module>>>
99 return std::move(bcFiles);
110 std::optional<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) -> std::optional<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();
132 return TmpFile(filePath, llvm::FileRemover(filePath.c_str()));
135 std::optional<TmpFile> asmFile = createTemp(basename,
"asm");
136 std::optional<TmpFile> binFile = createTemp(basename,
"");
137 std::optional<TmpFile> logFile = createTemp(basename,
"log");
138 if (!logFile || !asmFile || !binFile)
143 llvm::raw_fd_ostream asmStream(asmFile->first, ec);
145 emitError(loc) <<
"Couldn't open the file: `" << asmFile->first
146 <<
"`, error message: " << ec.message();
150 if (asmStream.has_error()) {
151 emitError(loc) <<
"An error occurred while writing the assembly to: `"
152 << asmFile->first <<
"`.";
158 std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>> cmdOpts =
161 const std::string cmdOptsStr =
"\"" + llvm::join(cmdOpts.second,
" ") +
"\"";
163 {
"ocloc",
"compile",
"-file", asmFile->first, inputFormat,
"-device",
164 getTarget().getChip(),
"-output", binFile->first,
"-output_no_suffix",
165 "-options", cmdOptsStr});
168 #define DEBUG_TYPE "serialize-to-binary"
170 llvm::dbgs() <<
"Tool invocation for module: "
172 llvm::interleave(oclocArgs, llvm::dbgs(),
" ");
173 llvm::dbgs() <<
"\n";
180 if (message.empty()) {
181 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> toolStderr =
182 llvm::MemoryBuffer::getFile(logFile->first);
184 emitError(loc) << toolName <<
" invocation failed. Log:\n"
185 << toolStderr->get()->getBuffer();
187 emitError(loc) << toolName <<
" invocation failed.";
191 <<
" invocation failed, error message: " << message;
194 std::optional<StringRef> redirects[] = {
200 if (llvm::sys::ExecuteAndWait(oclocCompiler.value(), oclocArgs, std::nullopt,
201 redirects, 0, 0, &message))
202 return emitLogError(
"`ocloc`");
203 binFile->first.append(
".bin");
204 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> binaryBuffer =
205 llvm::MemoryBuffer::getFile(binFile->first);
207 emitError(loc) <<
"Couldn't open the file: `" << binFile->first
208 <<
"`, error message: " << binaryBuffer.getError().message();
211 StringRef bin = (*binaryBuffer)->getBuffer();
219 if (!pathRef.empty()) {
220 path.insert(path.begin(), pathRef.begin(), pathRef.end());
221 llvm::sys::path::append(path,
"bin", tool);
222 if (llvm::sys::fs::can_execute(path))
223 return StringRef(path.data(), path.size()).str();
226 if (std::optional<std::string> toolPath =
227 llvm::sys::Process::FindInEnvPath(
"PATH", tool))
231 <<
"Couldn't find the `" << tool
232 <<
"` binary. Please specify the toolkit "
233 "path via GpuModuleToBinaryPass or add the compiler to $PATH`.";
240 SPIRVSerializer(
Operation &module, XeVMTargetAttr xeTarget,
248 std::optional<SmallVector<char, 0>>
249 moduleToObject(llvm::Module &llvmModule)
override;
254 std::optional<std::string>
255 translateToSPIRVBinary(llvm::Module &llvmModule,
256 llvm::TargetMachine &targetMachine);
260 void SPIRVSerializer::init() {
261 static llvm::once_flag initializeBackendOnce;
262 llvm::call_once(initializeBackendOnce, []() {
263 #if LLVM_HAS_SPIRV_TARGET
264 LLVMInitializeSPIRVTarget();
265 LLVMInitializeSPIRVTargetInfo();
266 LLVMInitializeSPIRVTargetMC();
267 LLVMInitializeSPIRVAsmPrinter();
272 std::optional<SmallVector<char, 0>>
273 SPIRVSerializer::moduleToObject(llvm::Module &llvmModule) {
274 #define DEBUG_TYPE "serialize-to-llvm"
276 llvm::dbgs() <<
"LLVM IR for module: " << getGPUModuleOp().getNameAttr()
278 llvm::dbgs() << llvmModule <<
"\n";
279 llvm::dbgs().flush();
284 if (targetOptions.getCompilationTarget() == gpu::CompilationTarget::Offload)
287 #if !LLVM_HAS_SPIRV_TARGET
288 getGPUModuleOp()->emitError(
"The `SPIRV` target was not built. Please enable "
289 "it when building LLVM.");
293 std::optional<llvm::TargetMachine *> targetMachine =
294 getOrCreateTargetMachine();
295 if (!targetMachine) {
296 getGPUModuleOp().emitError() <<
"Target Machine unavailable for triple "
297 << triple <<
", can't optimize with LLVM\n";
302 if (targetOptions.getCompilationTarget() ==
303 gpu::CompilationTarget::Assembly) {
304 std::optional<std::string> serializedISA =
305 translateToISA(llvmModule, **targetMachine);
306 if (!serializedISA) {
307 getGPUModuleOp().emitError() <<
"Failed translating the module to ISA."
308 << triple <<
", can't compile with LLVM\n";
312 #define DEBUG_TYPE "serialize-to-isa"
314 llvm::dbgs() <<
"SPIR-V for module: " << getGPUModuleOp().getNameAttr()
316 llvm::dbgs() << *serializedISA <<
"\n";
317 llvm::dbgs().flush();
322 StringRef bin(serializedISA->c_str(), serializedISA->size() + 1);
331 std::optional<std::string> serializedSPIRVBinary =
332 translateToSPIRVBinary(llvmModule, **targetMachine);
333 if (!serializedSPIRVBinary) {
334 getGPUModuleOp().emitError() <<
"Failed translating the module to Binary.";
337 if (serializedSPIRVBinary->size() % 4) {
338 getGPUModuleOp().emitError() <<
"SPIRV code size must be a multiple of 4.";
341 StringRef bin(serializedSPIRVBinary->c_str(), serializedSPIRVBinary->size());
345 std::optional<std::string>
346 SPIRVSerializer::translateToSPIRVBinary(llvm::Module &llvmModule,
347 llvm::TargetMachine &targetMachine) {
348 std::string targetISA;
349 llvm::raw_string_ostream stream(targetISA);
352 llvm::buffer_ostream pstream(stream);
353 llvm::legacy::PassManager codegenPasses;
354 if (targetMachine.addPassesToEmitFile(codegenPasses, pstream,
nullptr,
355 llvm::CodeGenFileType::ObjectFile))
358 codegenPasses.run(llvmModule);
363 std::optional<SmallVector<char, 0>>
368 auto gpuMod = dyn_cast<gpu::GPUModuleOp>(module);
370 module->
emitError(
"expected to be a gpu.module op");
373 auto xeTarget = cast<XeVMTargetAttr>(attribute);
374 if (xeTarget.getTriple().starts_with(
"spirv")) {
375 gpuMod.walk([&](LLVM::LLVMFuncOp funcOp) {
376 if (funcOp->hasAttr(gpu::GPUDialect::getKernelFuncAttrName())) {
377 funcOp.setIntelReqdSubGroupSize(16);
378 return WalkResult::interrupt();
383 SPIRVSerializer serializer(*module, cast<XeVMTargetAttr>(attribute),
387 #if !LLVM_HAS_SPIRV_TARGET
388 module->
emitError(
"Cannot run `TargetRegistry::lookupTarget()` for SPIRV "
389 "without having the target built.");
392 return serializer.run();
394 module->
emitError(
"Unsupported XeVM target triple: ") << xeTarget.getTriple();
403 gpu::CompilationTarget format =
options.getCompilationTarget();
404 auto xeTarget = cast<XeVMTargetAttr>(attribute);
406 if (format == gpu::CompilationTarget::Assembly)
407 properties.push_back(
408 builder.getNamedAttr(
"O", builder.getI32IntegerAttr(xeTarget.getO())));
410 DictionaryAttr objectProps;
411 if (!properties.empty())
412 objectProps = builder.getDictionaryAttr(properties);
414 return builder.getAttr<gpu::ObjectAttr>(
416 builder.getStringAttr(StringRef(
object.data(),
object.size())),
417 objectProps,
nullptr);
static llvm::ManagedStatic< PassManagerOptions > options
Attributes are known-constant values of operations.
MLIRContext * getContext() const
Return the context this attribute belongs to.
This class is a general helper class for creating context-global objects like types,...
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 std::optional< 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.
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.
MLIRContext * getContext()
Return the context this operation is associated with.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
static WalkResult advance()
This class serves as an opaque interface for passing options to the TargetAttrInterface methods.
std::pair< llvm::BumpPtrAllocator, SmallVector< const char * > > tokenizeCmdOptions() const
Returns a tokenization of the command line options.
StringRef getToolkitPath() const
Returns the toolkit path.
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.
std::optional< SmallVector< std::unique_ptr< llvm::Module > > > loadBitcodeFiles(llvm::Module &module) override
Loads the bitcode files in librariesToLink.
std::optional< SmallVector< char, 0 > > compileToBinary(const std::string &asmStr, StringRef inputFormat)
Compiles to native code using ocloc.
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.