[Mlir-commits] [mlir] [XeVM] Use `ocloc` for binary generation. (PR #188331)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Tue Mar 24 12:58:34 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir
Author: Md Abdullah Shahneous Bari (mshahneo)
<details>
<summary>Changes</summary>
XeVM currently doesn't support native binary generation. This PR enables Ahead of Time (AOT) compilation of gpu module to native binary using `ocloc`.
Currently, only works with LevelZeroRuntimeWrappers.
---
Full diff: https://github.com/llvm/llvm-project/pull/188331.diff
2 Files Affected:
- (modified) mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp (+15-8)
- (modified) mlir/lib/Target/LLVM/XeVM/Target.cpp (+19-6)
``````````diff
diff --git a/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
index 7859fb3feaabc..01965da038820 100644
--- a/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
+++ b/mlir/lib/ExecutionEngine/LevelZeroRuntimeWrappers.cpp
@@ -12,6 +12,7 @@
#include "level_zero/ze_api.h"
#include <cassert>
+#include <cstring>
#include <deque>
#include <exception>
#include <functional>
@@ -379,16 +380,15 @@ struct StreamWrapper {
}
};
-static ze_module_handle_t loadModule(const void *data, size_t dataSize) {
+static ze_module_handle_t
+loadModule(const void *data, size_t dataSize,
+ ze_module_format_t format = ZE_MODULE_FORMAT_NATIVE) {
assert(data);
ze_module_handle_t zeModule;
- ze_module_desc_t desc = {ZE_STRUCTURE_TYPE_MODULE_DESC,
- nullptr,
- ZE_MODULE_FORMAT_IL_SPIRV,
- dataSize,
- (const uint8_t *)data,
- nullptr,
- nullptr};
+ ze_module_desc_t desc = {
+ ZE_STRUCTURE_TYPE_MODULE_DESC, nullptr, format, dataSize,
+ (const uint8_t *)data, nullptr, nullptr};
+
ze_module_build_log_handle_t buildLogHandle;
ze_result_t result =
zeModuleCreate(getRtContext().context.get(), getRtContext().device, &desc,
@@ -520,6 +520,13 @@ extern "C" ze_module_handle_t mgpuModuleLoad(const void *data,
return catchAll([&]() { return loadModule(data, gpuBlobSize); });
}
+extern "C" ze_module_handle_t mgpuModuleLoadJIT(void *data, int optLevel) {
+ return catchAll([&]() {
+ return loadModule(data, strlen(reinterpret_cast<char *>(data)),
+ ZE_MODULE_FORMAT_IL_SPIRV);
+ });
+}
+
extern "C" ze_kernel_handle_t mgpuModuleGetFunction(ze_module_handle_t module,
const char *name) {
assert(module && name);
diff --git a/mlir/lib/Target/LLVM/XeVM/Target.cpp b/mlir/lib/Target/LLVM/XeVM/Target.cpp
index e7c4f1764bb37..83eec5e9d5549 100644
--- a/mlir/lib/Target/LLVM/XeVM/Target.cpp
+++ b/mlir/lib/Target/LLVM/XeVM/Target.cpp
@@ -107,9 +107,8 @@ gpu::GPUModuleOp SerializeGPUModuleBase::getGPUModuleOp() {
// There are 2 ways to access IGC: AOT (ocloc) and JIT (L0 runtime).
// - L0 runtime consumes IL and is external to MLIR codebase (rt wrappers).
// - `ocloc` tool can be "queried" from within MLIR.
-FailureOr<SmallVector<char, 0>>
-SerializeGPUModuleBase::compileToBinary(StringRef asmStr,
- StringRef inputFormat) {
+FailureOr<SmallVector<char, 0>> SerializeGPUModuleBase::compileToBinary(
+ StringRef asmStr, StringRef inputFormat = "-spirv_input") {
using TmpFile = std::pair<llvm::SmallString<128>, llvm::FileRemover>;
// Find the `ocloc` tool.
std::optional<std::string> oclocCompiler = findTool("ocloc");
@@ -341,7 +340,20 @@ SPIRVSerializer::moduleToObject(llvm::Module &llvmModule) {
return SmallVector<char, 0>(bin.begin(), bin.end());
}
- // Level zero runtime is set up to accept SPIR-V binary
+ // Binary generation path for SPIR-V target. Optimization and SPIR-V
+ // extensions are enabled in this path. In this path, first the SPIR-V binary
+ // is generated directly using the SPIR-V backends `SPIRVTranslateModule` API.
+ // Resultant SPIR-V is then fed to `ocloc` compiler (Intel's OpenCL Offline
+ // Compiler) to generate the final binary for Intel GPUs.
+
+ // @TODO: This part is doing exact same SPIR-V code generation as the previous
+ // section under (targetOptions.getCompilationTarget() ==
+ // gpu::CompilationTarget::Assembly) condition. Only execption is, it enables
+ // optimization and SPIRV extensions support for SPIRV binary output. We need
+ // to decide which one do we use for our SPIRV code generation, and remove the
+ // other one to avoid confusion. For now, we keep both to have more
+ // flexibility for testing and comparison.
+
std::string serializedSPIRVBinary;
std::string ErrMsg;
std::vector<std::string> Opts;
@@ -361,8 +373,9 @@ SPIRVSerializer::moduleToObject(llvm::Module &llvmModule) {
return getGPUModuleOp().emitError()
<< "SPIRV code size must be a multiple of 4.";
- StringRef bin(serializedSPIRVBinary.c_str(), serializedSPIRVBinary.size());
- return SmallVector<char, 0>(bin.begin(), bin.end());
+ StringRef spirvBin(serializedSPIRVBinary.c_str(),
+ serializedSPIRVBinary.size());
+ return compileToBinary(spirvBin, "-spirv_input");
#endif // LLVM_HAS_SPIRV_TARGET
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/188331
More information about the Mlir-commits
mailing list