[llvm] [Offload][L0] Add support for OffloadBinary format in L0 plugin (PR #185404)

Alex Duran via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 9 06:10:14 PDT 2026


https://github.com/adurang updated https://github.com/llvm/llvm-project/pull/185404

>From 131c2f234b8a5db526157856f861e30038be5b39 Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Mon, 9 Mar 2026 03:23:25 -0700
Subject: [PATCH 1/2] [Offload][L0] Add support for OffloadBinary format in L0
 plugin

---
 .../common/include/PluginInterface.h          |  4 ++
 .../common/src/PluginInterface.cpp            | 38 ++++++++++
 .../level_zero/src/L0Program.cpp              | 71 ++++++++++++++++++-
 3 files changed, 112 insertions(+), 1 deletion(-)

diff --git a/offload/plugins-nextgen/common/include/PluginInterface.h b/offload/plugins-nextgen/common/include/PluginInterface.h
index 1c59ed1eda841..c2aba32376cac 100644
--- a/offload/plugins-nextgen/common/include/PluginInterface.h
+++ b/offload/plugins-nextgen/common/include/PluginInterface.h
@@ -1358,6 +1358,10 @@ struct GenericPluginTy {
   /// target architecture.
   Expected<bool> checkBitcodeImage(StringRef Image) const;
 
+  /// Return true if the \p Image has OffloadBinary magic bytes.
+  /// The actual compatibility and parsing will be handled by the runtime.
+  Expected<bool> checkOffloadBinaryImage(StringRef Image) const;
+
   /// Indicate if an image is compatible with the plugin devices. Notice that
   /// this function may be called before actually initializing the devices. So
   /// we could not move this function into GenericDeviceTy.
diff --git a/offload/plugins-nextgen/common/src/PluginInterface.cpp b/offload/plugins-nextgen/common/src/PluginInterface.cpp
index f681213b38794..3214538f57657 100644
--- a/offload/plugins-nextgen/common/src/PluginInterface.cpp
+++ b/offload/plugins-nextgen/common/src/PluginInterface.cpp
@@ -28,6 +28,7 @@
 
 #include "llvm/Bitcode/BitcodeReader.h"
 #include "llvm/Frontend/OpenMP/OMPConstants.h"
+#include "llvm/Object/OffloadBinary.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/JSON.h"
 #include "llvm/Support/MathExtras.h"
@@ -1624,6 +1625,31 @@ Expected<bool> GenericPluginTy::checkBitcodeImage(StringRef Image) const {
   return M.getTargetTriple().getArch() == getTripleArch();
 }
 
+Expected<bool> GenericPluginTy::checkOffloadBinaryImage(StringRef Image) const {
+  // Check if the image has OffloadBinary magic bytes
+  if (identify_magic(Image) != file_magic::offload_binary)
+    return false;
+
+  // Parse the OffloadBinary to check triple compatibility
+  MemoryBufferRef Buffer(Image, "offload_binary");
+  auto BinariesOrErr = OffloadBinary::create(Buffer);
+  if (!BinariesOrErr)
+    return BinariesOrErr.takeError();
+
+  // Check if any of the binaries match this plugin's architecture
+  auto &Binaries = *BinariesOrErr;
+  Triple::ArchType PluginArch = getTripleArch();
+
+  for (const auto &Binary : Binaries) {
+    StringRef Triple = Binary->getTriple();
+    llvm::Triple T(Triple);
+    if (T.getArch() == PluginArch)
+      return true;
+  }
+
+  return false;
+}
+
 int32_t GenericPluginTy::is_initialized() const { return Initialized; }
 
 int32_t GenericPluginTy::isPluginCompatible(StringRef Image) {
@@ -1651,6 +1677,12 @@ int32_t GenericPluginTy::isPluginCompatible(StringRef Image) {
       return HandleError(std::move(Err));
     return *MatchOrErr;
   }
+  case file_magic::offload_binary: {
+    auto MatchOrErr = checkOffloadBinaryImage(Image);
+    if (Error Err = MatchOrErr.takeError())
+      return HandleError(std::move(Err));
+    return *MatchOrErr;
+  }
   default:
     return false;
   }
@@ -1688,6 +1720,12 @@ int32_t GenericPluginTy::isDeviceCompatible(int32_t DeviceId, StringRef Image) {
       return HandleError(std::move(Err));
     return *MatchOrErr;
   }
+  case file_magic::offload_binary: {
+    auto MatchOrErr = checkOffloadBinaryImage(Image);
+    if (Error Err = MatchOrErr.takeError())
+      return HandleError(std::move(Err));
+    return *MatchOrErr;
+  }
   default:
     return false;
   }
diff --git a/offload/plugins-nextgen/level_zero/src/L0Program.cpp b/offload/plugins-nextgen/level_zero/src/L0Program.cpp
index 9c914f0eb7cab..ea272ff1cb279 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Program.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Program.cpp
@@ -215,8 +215,76 @@ bool isValidOneOmpImage(StringRef Image, uint64_t &MajorVer,
 Error L0ProgramBuilderTy::buildModules(const std::string_view BuildOptions) {
   auto &l0Device = getL0Device();
   auto Image = getMemoryBuffer();
+
+  // Check if image is an inner OffloadBinary (nested format)
+  if (identify_magic(Image.getBuffer()) == file_magic::offload_binary) {
+    ODBG(OLDT_Module) << "Processing nested OffloadBinary image\n";
+
+    // Parse inner OffloadBinary
+    auto InnerBinariesOrErr = llvm::object::OffloadBinary::create(Image);
+    if (!InnerBinariesOrErr)
+      return Plugin::error(ErrorCode::UNKNOWN,
+                          "Failed to parse inner OffloadBinary: %s",
+                          llvm::toString(InnerBinariesOrErr.takeError()).c_str());
+
+    auto &InnerBinaries = *InnerBinariesOrErr;
+
+    // Should contain exactly one image
+    if (InnerBinaries.size() != 1)
+      return Plugin::error(ErrorCode::UNKNOWN,
+                          "Expected single inner OffloadBinary entry, got %zu",
+                          InnerBinaries.size());
+
+    const llvm::object::OffloadBinary *InnerBinary = InnerBinaries[0].get();
+    llvm::object::ImageKind ImageKind = InnerBinary->getImageKind();
+
+    // Extract image data from inner binary
+    llvm::StringRef ImageData = InnerBinary->getImage();
+    const uint8_t *ImgBegin = reinterpret_cast<const uint8_t *>(ImageData.data());
+
+    // Read metadata from inner binary
+    llvm::StringRef Version = InnerBinary->getString("version");
+    llvm::StringRef Format = InnerBinary->getString("format");
+    llvm::StringRef CompileOpts = InnerBinary->getString("compile-opts");
+    llvm::StringRef LinkOpts = InnerBinary->getString("link-opts");
+
+    ODBG(OLDT_Module) << "Inner OffloadBinary metadata: version=" << Version
+                      << ", format=" << Format << ", kind=" << ImageKind << "\n";
+
+    // Build options string combining BuildOptions with compile/link opts
+    std::string Options(BuildOptions);
+    if (!CompileOpts.empty() || !LinkOpts.empty()) {
+      if (!CompileOpts.empty())
+        Options += " " + CompileOpts.str();
+      if (!LinkOpts.empty())
+        Options += " " + LinkOpts.str();
+      replaceDriverOptsWithBackendOpts(l0Device, Options);
+      ODBG(OLDT_Module) << "Using compile options: " << CompileOpts
+                        << ", link options: " << LinkOpts << "\n";
+    }
+
+    // Determine module format based on image kind
+    ze_module_format_t ModuleFormat;
+    if (ImageKind == llvm::object::IMG_SPIRV) {
+      // SPIR-V intermediate language
+      ODBG(OLDT_Module) << "Loading SPIR-V module\n";
+      ModuleFormat = ZE_MODULE_FORMAT_IL_SPIRV;
+    } else if (ImageKind == llvm::object::IMG_Object) {
+      // Native binary format
+      ODBG(OLDT_Module) << "Loading native binary module\n";
+      ModuleFormat = ZE_MODULE_FORMAT_NATIVE;
+    } else {
+      return Plugin::error(ErrorCode::UNKNOWN,
+                          "Unsupported image kind %d in inner OffloadBinary",
+                          static_cast<int>(ImageKind));
+    }
+
+    // Load module into Level Zero
+    return addModule(ImageData.size(), ImgBegin, Options, ModuleFormat);
+  }
+
   if (identify_magic(Image.getBuffer()) == file_magic::spirv_object) {
-    // Handle legacy plain SPIR-V image.
+    ODBG(OLDT_Module) << "Processing raw SPIR-V image\n";
     const uint8_t *ImgBegin =
         reinterpret_cast<const uint8_t *>(Image.getBufferStart());
     return addModule(Image.getBufferSize(), ImgBegin, BuildOptions,
@@ -228,6 +296,7 @@ Error L0ProgramBuilderTy::buildModules(const std::string_view BuildOptions) {
     ODBG(OLDT_Module) << "Warning: image is not a valid oneAPI OpenMP image.";
     return Plugin::error(ErrorCode::UNKNOWN, "Invalid oneAPI OpenMP image");
   }
+  ODBG(OLDT_Module) << "Processing ELF-wrapped SPIR-V image\n";
 
   // Iterate over the images and pick the first one that fits.
   uint64_t ImageCount = 0;

>From b09b10cf18f60950b6bfa26d81703278f4845a5b Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Mon, 9 Mar 2026 06:09:57 -0700
Subject: [PATCH 2/2] format

---
 .../level_zero/src/L0Program.cpp              | 20 ++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/offload/plugins-nextgen/level_zero/src/L0Program.cpp b/offload/plugins-nextgen/level_zero/src/L0Program.cpp
index ea272ff1cb279..cf73d44129254 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Program.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Program.cpp
@@ -223,24 +223,25 @@ Error L0ProgramBuilderTy::buildModules(const std::string_view BuildOptions) {
     // Parse inner OffloadBinary
     auto InnerBinariesOrErr = llvm::object::OffloadBinary::create(Image);
     if (!InnerBinariesOrErr)
-      return Plugin::error(ErrorCode::UNKNOWN,
-                          "Failed to parse inner OffloadBinary: %s",
-                          llvm::toString(InnerBinariesOrErr.takeError()).c_str());
+      return Plugin::error(
+          ErrorCode::UNKNOWN, "Failed to parse inner OffloadBinary: %s",
+          llvm::toString(InnerBinariesOrErr.takeError()).c_str());
 
     auto &InnerBinaries = *InnerBinariesOrErr;
 
     // Should contain exactly one image
     if (InnerBinaries.size() != 1)
       return Plugin::error(ErrorCode::UNKNOWN,
-                          "Expected single inner OffloadBinary entry, got %zu",
-                          InnerBinaries.size());
+                           "Expected single inner OffloadBinary entry, got %zu",
+                           InnerBinaries.size());
 
     const llvm::object::OffloadBinary *InnerBinary = InnerBinaries[0].get();
     llvm::object::ImageKind ImageKind = InnerBinary->getImageKind();
 
     // Extract image data from inner binary
     llvm::StringRef ImageData = InnerBinary->getImage();
-    const uint8_t *ImgBegin = reinterpret_cast<const uint8_t *>(ImageData.data());
+    const uint8_t *ImgBegin =
+        reinterpret_cast<const uint8_t *>(ImageData.data());
 
     // Read metadata from inner binary
     llvm::StringRef Version = InnerBinary->getString("version");
@@ -249,7 +250,8 @@ Error L0ProgramBuilderTy::buildModules(const std::string_view BuildOptions) {
     llvm::StringRef LinkOpts = InnerBinary->getString("link-opts");
 
     ODBG(OLDT_Module) << "Inner OffloadBinary metadata: version=" << Version
-                      << ", format=" << Format << ", kind=" << ImageKind << "\n";
+                      << ", format=" << Format << ", kind=" << ImageKind
+                      << "\n";
 
     // Build options string combining BuildOptions with compile/link opts
     std::string Options(BuildOptions);
@@ -275,8 +277,8 @@ Error L0ProgramBuilderTy::buildModules(const std::string_view BuildOptions) {
       ModuleFormat = ZE_MODULE_FORMAT_NATIVE;
     } else {
       return Plugin::error(ErrorCode::UNKNOWN,
-                          "Unsupported image kind %d in inner OffloadBinary",
-                          static_cast<int>(ImageKind));
+                           "Unsupported image kind %d in inner OffloadBinary",
+                           static_cast<int>(ImageKind));
     }
 
     // Load module into Level Zero



More information about the llvm-commits mailing list