[llvm] [OFFLOAD] Generalize support for OffloadBinary images (PR #186088)

Alex Duran via llvm-commits llvm-commits at lists.llvm.org
Thu Mar 12 06:18:33 PDT 2026


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

>From fe2e96bb0c58ed3da76a17b9417a1869ee5f1c25 Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Thu, 12 Mar 2026 03:34:20 -0700
Subject: [PATCH 1/6] [OFFLOAD] Generalize support for OffloadBinary images

---
 offload/plugins-nextgen/amdgpu/src/rtl.cpp    |   3 +-
 .../common/include/PluginInterface.h          |  15 +-
 .../common/src/PluginInterface.cpp            | 166 ++++++++++++++++--
 offload/plugins-nextgen/cuda/src/rtl.cpp      |   3 +-
 offload/plugins-nextgen/host/src/rtl.cpp      |   3 +-
 .../level_zero/include/L0Device.h             |   3 +-
 .../level_zero/include/L0Program.h            |  11 +-
 .../level_zero/src/L0Device.cpp               |   6 +-
 .../level_zero/src/L0Plugin.cpp               |  33 +---
 .../level_zero/src/L0Program.cpp              |  54 ++----
 10 files changed, 202 insertions(+), 95 deletions(-)

diff --git a/offload/plugins-nextgen/amdgpu/src/rtl.cpp b/offload/plugins-nextgen/amdgpu/src/rtl.cpp
index bfd07b0919d08..f04923063c2fb 100644
--- a/offload/plugins-nextgen/amdgpu/src/rtl.cpp
+++ b/offload/plugins-nextgen/amdgpu/src/rtl.cpp
@@ -2381,7 +2381,8 @@ struct AMDGPUDeviceTy : public GenericDeviceTy, AMDGenericDeviceTy {
   /// Load the binary image into the device and allocate an image object.
   Expected<DeviceImageTy *>
   loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
-                 int32_t ImageId) override {
+                 int32_t ImageId,
+                 const OffloadBinMetadataTy *Metadata) override {
     // Allocate and initialize the image object.
     AMDGPUDeviceImageTy *AMDImage = Plugin.allocate<AMDGPUDeviceImageTy>();
     new (AMDImage) AMDGPUDeviceImageTy(ImageId, *this, std::move(TgtImage));
diff --git a/offload/plugins-nextgen/common/include/PluginInterface.h b/offload/plugins-nextgen/common/include/PluginInterface.h
index 83d20c620b96e..2e35b5ae9dbaa 100644
--- a/offload/plugins-nextgen/common/include/PluginInterface.h
+++ b/offload/plugins-nextgen/common/include/PluginInterface.h
@@ -41,8 +41,10 @@
 #endif
 
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/Frontend/OpenMP/OMPConstants.h"
 #include "llvm/Frontend/OpenMP/OMPGridValues.h"
+#include "llvm/Object/OffloadBinary.h"
 #include "llvm/Support/Allocator.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -301,10 +303,20 @@ struct InfoTreeNode {
   }
 };
 
+/// Metadata extracted from OffloadBinary format
+struct OffloadBinMetadataTy {
+  llvm::object::ImageKind ImageKind;
+  llvm::object::OffloadKind OffloadKind;
+  std::string Triple;
+  std::string Arch;
+  llvm::StringMap<std::string> StringData;
+};
+
 /// Class wrapping a __tgt_device_image and its offload entry table on a
 /// specific device. This class is responsible for storing and managing
 /// the offload entries for an image on a device.
 class DeviceImageTy {
+private:
   /// Image identifier within the corresponding device. Notice that this id is
   /// not unique between different device; they may overlap.
   int32_t ImageId;
@@ -781,7 +793,8 @@ struct GenericDeviceTy : public DeviceAllocatorTy {
   Expected<DeviceImageTy *> loadBinary(GenericPluginTy &Plugin,
                                        StringRef TgtImage);
   virtual Expected<DeviceImageTy *>
-  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage, int32_t ImageId) = 0;
+  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage, int32_t ImageId,
+                 const OffloadBinMetadataTy *Metadata) = 0;
 
   /// Unload a previously loaded Image from the device
   Error unloadBinary(DeviceImageTy *Image);
diff --git a/offload/plugins-nextgen/common/src/PluginInterface.cpp b/offload/plugins-nextgen/common/src/PluginInterface.cpp
index 21ba9db292c4c..d1dd78637ffc6 100644
--- a/offload/plugins-nextgen/common/src/PluginInterface.cpp
+++ b/offload/plugins-nextgen/common/src/PluginInterface.cpp
@@ -47,6 +47,39 @@ using namespace llvm::offload::debug;
 
 // TODO: Fix any thread safety issues for multi-threaded kernel recording.
 namespace llvm::omp::target::plugin {
+
+// Parse OffloadBinary and extract all inner images with metadata
+static Expected<SmallVector<std::pair<OffloadBinMetadataTy, StringRef>>>
+parseOffloadBinary(MemoryBufferRef Buffer) {
+  auto BinariesOrErr = llvm::object::OffloadBinary::create(Buffer);
+  if (!BinariesOrErr)
+    return BinariesOrErr.takeError();
+
+  auto &Binaries = *BinariesOrErr;
+
+  SmallVector<std::pair<OffloadBinMetadataTy, StringRef>> Results;
+  Results.reserve(Binaries.size());
+
+  for (const auto &BinaryPtr : Binaries) {
+    const llvm::object::OffloadBinary *Binary = BinaryPtr.get();
+
+    // Extract metadata for this entry
+    OffloadBinMetadataTy Metadata;
+    Metadata.ImageKind = Binary->getImageKind();
+    Metadata.OffloadKind = Binary->getOffloadKind();
+    Metadata.Triple = Binary->getTriple().str();
+    Metadata.Arch = Binary->getArch().str();
+    for (auto [Key, Value] : Binary->strings())
+      Metadata.StringData[Key] = Value.str();
+
+    StringRef InnerImage = Binary->getImage();
+
+    Results.emplace_back(std::move(Metadata), InnerImage);
+  }
+
+  return Results;
+}
+
 struct RecordReplayTy {
 
   // Describes the state of the record replay mechanism.
@@ -873,12 +906,28 @@ Error GenericDeviceTy::deinit(GenericPluginTy &Plugin) {
 
   return deinitImpl();
 }
+
+// Helper function to finalize image loading after loadBinaryImpl succeeds
+static Error finalizeImageLoad(GenericDeviceTy &Device, GenericPluginTy &Plugin,
+                                DeviceImageTy *Image) {
+  Device.LoadedImages.push_back(Image);
+
+  if (auto Err = Device.setupRPCServer(Plugin, *Image))
+    return Err;
+
+  if (auto Err = Device.callGlobalConstructors(Plugin, *Image))
+    return Err;
+
+  return Error::success();
+}
+
 Expected<DeviceImageTy *> GenericDeviceTy::loadBinary(GenericPluginTy &Plugin,
                                                       StringRef InputTgtImage) {
   ODBG(OLDT_Init) << "Load data from image "
                   << static_cast<const void *>(InputTgtImage.bytes_begin());
 
   std::unique_ptr<MemoryBuffer> Buffer;
+
   if (identify_magic(InputTgtImage) == file_magic::bitcode) {
     auto CompiledImageOrErr = Plugin.getJIT().process(InputTgtImage, *this);
     if (!CompiledImageOrErr) {
@@ -887,21 +936,89 @@ Expected<DeviceImageTy *> GenericDeviceTy::loadBinary(GenericPluginTy &Plugin,
                            "failure to jit IR image");
     }
     Buffer = std::move(*CompiledImageOrErr);
-  } else {
+  }
+  else if (identify_magic(InputTgtImage) == file_magic::offload_binary) {
+    MemoryBufferRef InputBuffer(InputTgtImage, "offload_binary");
+
+    auto ParsedOrErr = parseOffloadBinary(InputBuffer);
+    if (!ParsedOrErr)
+      return ParsedOrErr.takeError();
+
+    auto &InnerImages = *ParsedOrErr;
+
+    DeviceImageTy *FirstLoadedImage = nullptr;
+    Error LoadErrors = Error::success();
+
+    for (auto &[ExtractedMetadata, InnerImage] : InnerImages) {
+      if (identify_magic(InnerImage) == file_magic::offload_binary) {
+        auto ImageOrErr = loadBinary(Plugin, InnerImage);
+        if (ImageOrErr) {
+          if (!FirstLoadedImage)
+            FirstLoadedImage = *ImageOrErr;
+        } else {
+          LoadErrors = joinErrors(std::move(LoadErrors), ImageOrErr.takeError());
+        }
+        continue;
+      }
+
+      int32_t Compatible = Plugin.isDeviceCompatible(DeviceId, InnerImage);
+      if (!Compatible)
+        continue;  // Not compatible, skip
+
+      auto InnerBuffer = MemoryBuffer::getMemBufferCopy(InnerImage);
+      const OffloadBinMetadataTy *MetadataPtr = &ExtractedMetadata;
+
+      auto ImageOrErr = loadBinaryImpl(std::move(InnerBuffer), LoadedImages.size(),
+                                        MetadataPtr);
+      if (ImageOrErr) {
+        DeviceImageTy *LoadedImage = *ImageOrErr;
+
+        if (auto Err = finalizeImageLoad(*this, Plugin, LoadedImage)) {
+          LoadErrors = joinErrors(std::move(LoadErrors), std::move(Err));
+          continue;
+        }
+
+#ifdef OMPT_SUPPORT
+        if (ompt::Initialized) {
+          size_t Bytes = InnerImage.size();
+          performOmptCallback(
+              device_load, Plugin.getUserId(DeviceId),
+              /*FileName=*/nullptr, /*FileOffset=*/0, /*VmaInFile=*/nullptr,
+              /*ImgSize=*/Bytes,
+              /*HostAddr=*/const_cast<unsigned char *>(InnerImage.bytes_begin()),
+              /*DeviceAddr=*/nullptr, /* FIXME: ModuleId */ 0);
+        }
+#endif
+
+        if (!FirstLoadedImage)
+          FirstLoadedImage = LoadedImage;
+
+      } else {
+        LoadErrors = joinErrors(std::move(LoadErrors), ImageOrErr.takeError());
+      }
+    }
+
+    if (!FirstLoadedImage) {
+      if (LoadErrors)
+        return std::move(LoadErrors);
+      return Plugin::error(ErrorCode::UNKNOWN,
+                           "No compatible image found in OffloadBinary");
+    }
+
+    return FirstLoadedImage;
+  }
+  else {
     Buffer = MemoryBuffer::getMemBufferCopy(InputTgtImage);
   }
 
-  // Load the binary and allocate the image object. Use the next available id
-  // for the image id, which is the number of previously loaded images.
-  auto ImageOrErr = loadBinaryImpl(std::move(Buffer), LoadedImages.size());
+  auto ImageOrErr = loadBinaryImpl(std::move(Buffer), LoadedImages.size(),
+                                    nullptr);
   if (!ImageOrErr)
     return ImageOrErr.takeError();
   DeviceImageTy *Image = *ImageOrErr;
 
-  // Add the image to list.
-  LoadedImages.push_back(Image);
-
-  if (auto Err = setupRPCServer(Plugin, *Image))
+  // Finalize image load (track, setup RPC, call constructors)
+  if (auto Err = finalizeImageLoad(*this, Plugin, Image))
     return std::move(Err);
 
 #ifdef OMPT_SUPPORT
@@ -916,10 +1033,6 @@ Expected<DeviceImageTy *> GenericDeviceTy::loadBinary(GenericPluginTy &Plugin,
   }
 #endif
 
-  // Call any global constructors present on the device.
-  if (auto Err = callGlobalConstructors(Plugin, *Image))
-    return std::move(Err);
-
   // Return the pointer to the table of entries.
   return Image;
 }
@@ -1651,6 +1764,21 @@ int32_t GenericPluginTy::isPluginCompatible(StringRef Image) {
       return HandleError(std::move(Err));
     return *MatchOrErr;
   }
+  case file_magic::offload_binary: {
+    // Unwrap OffloadBinary and check if ANY inner image is compatible
+    auto ParsedOrErr = parseOffloadBinary(MemoryBufferRef(Image, "offload_binary"));
+    if (Error Err = ParsedOrErr.takeError())
+      return HandleError(std::move(Err));
+
+    auto &InnerImages = *ParsedOrErr;
+
+    for (auto &[Metadata, InnerImage] : InnerImages) {
+      if (isPluginCompatible(InnerImage))
+        return true;
+    }
+
+    return false;
+  }
   default:
     auto MatchOrErr = isImageCompatible(Image);
     if (Error Err = MatchOrErr.takeError())
@@ -1691,6 +1819,20 @@ int32_t GenericPluginTy::isDeviceCompatible(int32_t DeviceId, StringRef Image) {
       return HandleError(std::move(Err));
     return *MatchOrErr;
   }
+  case file_magic::offload_binary: {
+    auto ParsedOrErr = parseOffloadBinary(MemoryBufferRef(Image, "offload_binary"));
+    if (Error Err = ParsedOrErr.takeError())
+      return HandleError(std::move(Err));
+
+    auto &InnerImages = *ParsedOrErr;
+
+    for (auto &[Metadata, InnerImage] : InnerImages) {
+      if (isDeviceCompatible(DeviceId, InnerImage))
+        return true;
+    }
+
+    return false;
+  }
   default:
     auto MatchOrErr = isImageCompatible(DeviceId, Image);
     if (Error Err = MatchOrErr.takeError())
diff --git a/offload/plugins-nextgen/cuda/src/rtl.cpp b/offload/plugins-nextgen/cuda/src/rtl.cpp
index c50c70a4456fa..3f3d17f0fe882 100644
--- a/offload/plugins-nextgen/cuda/src/rtl.cpp
+++ b/offload/plugins-nextgen/cuda/src/rtl.cpp
@@ -581,7 +581,8 @@ struct CUDADeviceTy : public GenericDeviceTy {
   /// Load the binary image into the device and allocate an image object.
   Expected<DeviceImageTy *>
   loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
-                 int32_t ImageId) override {
+                 int32_t ImageId,
+                 const OffloadBinMetadataTy *Metadata) override {
     if (auto Err = setContext())
       return std::move(Err);
 
diff --git a/offload/plugins-nextgen/host/src/rtl.cpp b/offload/plugins-nextgen/host/src/rtl.cpp
index df2b6f2c1dba9..a4376d03db1b8 100644
--- a/offload/plugins-nextgen/host/src/rtl.cpp
+++ b/offload/plugins-nextgen/host/src/rtl.cpp
@@ -201,7 +201,8 @@ struct GenELF64DeviceTy : public GenericDeviceTy {
   /// Load the binary image into the device and allocate an image object.
   Expected<DeviceImageTy *>
   loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
-                 int32_t ImageId) override {
+                 int32_t ImageId,
+                 const OffloadBinMetadataTy *Metadata) override {
     // Allocate and initialize the image object.
     GenELF64DeviceImageTy *Image = Plugin.allocate<GenELF64DeviceImageTy>();
     new (Image) GenELF64DeviceImageTy(ImageId, *this, std::move(TgtImage));
diff --git a/offload/plugins-nextgen/level_zero/include/L0Device.h b/offload/plugins-nextgen/level_zero/include/L0Device.h
index 001a41ba77d7b..f72ca28b414f9 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Device.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Device.h
@@ -550,7 +550,8 @@ class L0DeviceTy final : public GenericDeviceTy {
   // Generic device interface implementation.
   Expected<DeviceImageTy *>
   loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
-                 int32_t ImageId) override;
+                 int32_t ImageId,
+                 const OffloadBinMetadataTy *Metadata) override;
   Error unloadBinaryImpl(DeviceImageTy *Image) override;
   Expected<void *> allocate(size_t Size, void *HstPtr,
                             TargetAllocTy Kind) override;
diff --git a/offload/plugins-nextgen/level_zero/include/L0Program.h b/offload/plugins-nextgen/level_zero/include/L0Program.h
index 2658ab10ca5c1..3cac93d5138c1 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Program.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Program.h
@@ -22,6 +22,8 @@ class L0DeviceTy;
 class L0ProgramBuilderTy {
   L0DeviceTy &Device;
   std::unique_ptr<MemoryBuffer> Image;
+  const OffloadBinMetadataTy *Metadata;
+
   /// Handle multiple modules within a single target image.
   llvm::SmallVector<ze_module_handle_t> Modules;
 
@@ -39,8 +41,12 @@ class L0ProgramBuilderTy {
   Error linkModules();
 
 public:
-  L0ProgramBuilderTy(L0DeviceTy &Device, std::unique_ptr<MemoryBuffer> &&Image)
-      : Device(Device), Image(std::move(Image)) {}
+  L0ProgramBuilderTy(L0DeviceTy &Device, std::unique_ptr<MemoryBuffer> &&Image,
+                     int32_t ImageId,
+                     const OffloadBinMetadataTy *Metadata)
+      : Device(Device), Image(std::move(Image)), Metadata(Metadata) {
+    (void)ImageId; // Unused parameter
+  }
   ~L0ProgramBuilderTy() = default;
 
   L0DeviceTy &getL0Device() const { return Device; }
@@ -48,6 +54,7 @@ class L0ProgramBuilderTy {
   llvm::SmallVector<ze_module_handle_t> &getModules() { return Modules; }
 
   MemoryBufferRef getMemoryBuffer() const { return MemoryBufferRef(*Image); }
+  const OffloadBinMetadataTy *getMetadata() const { return Metadata; }
   Error buildModules(const std::string_view BuildOptions);
 
   /// Retrieve the ELF binary for the program.
diff --git a/offload/plugins-nextgen/level_zero/src/L0Device.cpp b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
index 4db3c4e47f544..5f7c8cba47ec1 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Device.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Device.cpp
@@ -234,7 +234,8 @@ Error L0DeviceTy::deinitImpl() {
 
 Expected<DeviceImageTy *>
 L0DeviceTy::loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
-                           int32_t ImageId) {
+                           int32_t ImageId,
+                           const OffloadBinMetadataTy *Metadata) {
   auto *PGM = getProgramFromImage(TgtImage->getMemBufferRef());
   if (PGM) {
     // Program already exists.
@@ -255,7 +256,8 @@ L0DeviceTy::loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
   CompilationOptions += " ";
   CompilationOptions += Options.InternalCompilationOptions;
 
-  L0ProgramBuilderTy Builder(*this, std::move(TgtImage));
+  // Pass metadata to builder
+  L0ProgramBuilderTy Builder(*this, std::move(TgtImage), ImageId, Metadata);
   if (auto Err = Builder.buildModules(CompilationOptions))
     return std::move(Err);
 
diff --git a/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp b/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp
index 7dc0b919ca5a5..eaf2dbd295dc2 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp
@@ -165,38 +165,7 @@ Expected<bool> LevelZeroPluginTy::isELFCompatible(uint32_t DeviceId,
 
 // We only need to check for formats other than ELF here.
 Expected<bool> LevelZeroPluginTy::isImageCompatible(StringRef Image) const {
-  switch (identify_magic(Image)) {
-  case file_magic::spirv_object:
-    // Handle SPIRV objects directly
-    return true;
-  case file_magic::offload_binary: {
-    // Handle OffloadBinary format
-    MemoryBufferRef Buffer(Image, "offload_binary");
-    auto BinariesOrErr = OffloadBinary::create(Buffer);
-    if (!BinariesOrErr)
-      return BinariesOrErr.takeError();
-
-    auto &Binaries = *BinariesOrErr;
-    if (Binaries.size() != 1)
-      return false;
-
-    const OffloadBinary *InnerBinary = Binaries[0].get();
-    ImageKind ImageKind = InnerBinary->getImageKind();
-    llvm::Triple Triple(InnerBinary->getTriple());
-
-    if (Triple.getArch() != getTripleArch())
-      return false;
-
-    if (ImageKind != llvm::object::IMG_SPIRV &&
-        ImageKind != llvm::object::IMG_Object)
-      return false;
-
-    return true;
-  }
-  default:
-    // Unknown format
-    return false;
-  }
+  return identify_magic(Image) == file_magic::spirv_object;
 }
 
 Error LevelZeroPluginTy::syncBarrierImpl(omp_interop_val_t *Interop) {
diff --git a/offload/plugins-nextgen/level_zero/src/L0Program.cpp b/offload/plugins-nextgen/level_zero/src/L0Program.cpp
index 60a71c9ad65a2..9831452cc3811 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Program.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Program.cpp
@@ -216,73 +216,43 @@ 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";
+  // Use metadata passed during loading if available
+  if (Metadata) {
+    llvm::object::ImageKind ImageKind = Metadata->ImageKind;
+    std::string CompileOpts = Metadata->StringData.lookup("compile-opts");
+    std::string LinkOpts = Metadata->StringData.lookup("link-opts");
 
-    // 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());
+    ODBG(OLDT_Module) << "Using OffloadBinary metadata: kind=" << ImageKind;
 
-    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 CompileOpts = InnerBinary->getString("compile-opts");
-    llvm::StringRef LinkOpts = InnerBinary->getString("link-opts");
-
-    ODBG(OLDT_Module) << "Inner OffloadBinary metadata: version=" << Version
-                      << ", kind=" << ImageKind;
-
-    // Build options string combining BuildOptions with compile/link opts
     std::string Options(BuildOptions);
     if (!CompileOpts.empty() || !LinkOpts.empty()) {
       if (!CompileOpts.empty())
-        Options += " " + CompileOpts.str();
+        Options += " " + CompileOpts;
       if (!LinkOpts.empty())
-        Options += " " + LinkOpts.str();
+        Options += " " + LinkOpts;
       replaceDriverOptsWithBackendOpts(l0Device, Options);
       ODBG(OLDT_Module) << "Using compile options: " << CompileOpts
                         << ", link options: " << LinkOpts;
     }
 
-    // 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";
       ModuleFormat = ZE_MODULE_FORMAT_IL_SPIRV;
     } else if (ImageKind == llvm::object::IMG_Object) {
-      // Native binary format
       ODBG(OLDT_Module) << "Loading native binary module";
       ModuleFormat = ZE_MODULE_FORMAT_NATIVE;
     } else {
       return Plugin::error(ErrorCode::UNKNOWN,
-                           "Unsupported image kind %d in inner OffloadBinary",
+                           "Unsupported image kind %d in OffloadBinary metadata",
                            static_cast<int>(ImageKind));
     }
 
-    // Load module into Level Zero
-    return addModule(ImageData.size(), ImgBegin, Options, ModuleFormat);
+    const uint8_t *ImgBegin = (const uint8_t*)Image.getBufferStart();
+    return addModule(Image.getBufferSize(), ImgBegin, Options, ModuleFormat);
   }
 
+  // Fallback: Raw SPIRV without metadata (existing code unchanged)
   if (identify_magic(Image.getBuffer()) == file_magic::spirv_object) {
     ODBG(OLDT_Module) << "Processing raw SPIR-V image";
     const uint8_t *ImgBegin =

>From faa0d664d4dd4b436fb69e303aa59dd07cdbfd50 Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Thu, 12 Mar 2026 05:08:27 -0700
Subject: [PATCH 2/6] load only one compatible image

---
 offload/plugins-nextgen/common/src/PluginInterface.cpp | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/offload/plugins-nextgen/common/src/PluginInterface.cpp b/offload/plugins-nextgen/common/src/PluginInterface.cpp
index d1dd78637ffc6..32fadbef1ef12 100644
--- a/offload/plugins-nextgen/common/src/PluginInterface.cpp
+++ b/offload/plugins-nextgen/common/src/PluginInterface.cpp
@@ -990,9 +990,8 @@ Expected<DeviceImageTy *> GenericDeviceTy::loadBinary(GenericPluginTy &Plugin,
         }
 #endif
 
-        if (!FirstLoadedImage)
-          FirstLoadedImage = LoadedImage;
-
+        FirstLoadedImage = LoadedImage;
+        break;
       } else {
         LoadErrors = joinErrors(std::move(LoadErrors), ImageOrErr.takeError());
       }

>From 4122c01b8f5a9e3eb2f943cdc83e2fa06dfdf994 Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Thu, 12 Mar 2026 05:13:43 -0700
Subject: [PATCH 3/6] add message

---
 offload/plugins-nextgen/common/src/PluginInterface.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/offload/plugins-nextgen/common/src/PluginInterface.cpp b/offload/plugins-nextgen/common/src/PluginInterface.cpp
index 32fadbef1ef12..7965ecbc98a78 100644
--- a/offload/plugins-nextgen/common/src/PluginInterface.cpp
+++ b/offload/plugins-nextgen/common/src/PluginInterface.cpp
@@ -990,6 +990,8 @@ Expected<DeviceImageTy *> GenericDeviceTy::loadBinary(GenericPluginTy &Plugin,
         }
 #endif
 
+        ODBG(OLDT_Init) << "Found compatible image from OffloadBinary: "
+                        << static_cast<const void *>(InnerImage.bytes_begin());
         FirstLoadedImage = LoadedImage;
         break;
       } else {

>From 245ea8b6e534e6edb737be0572b4a27643ec18ac Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Thu, 12 Mar 2026 05:42:23 -0700
Subject: [PATCH 4/6] add metadata validation

---
 .../common/include/PluginInterface.h          |  9 +++++++++
 .../common/src/PluginInterface.cpp            | 15 ++++++++++++++
 .../level_zero/include/L0Plugin.h             |  3 +++
 .../level_zero/src/L0Plugin.cpp               | 20 +++++++++++++++++++
 4 files changed, 47 insertions(+)

diff --git a/offload/plugins-nextgen/common/include/PluginInterface.h b/offload/plugins-nextgen/common/include/PluginInterface.h
index 2e35b5ae9dbaa..a5a536cc4e4e6 100644
--- a/offload/plugins-nextgen/common/include/PluginInterface.h
+++ b/offload/plugins-nextgen/common/include/PluginInterface.h
@@ -1398,6 +1398,15 @@ struct GenericPluginTy {
     return isImageCompatible(Image);
   }
 
+  /// Validate OffloadBinary metadata for compatibility with the plugin.
+  /// This is called when unwrapping OffloadBinary to check if the metadata
+  /// (triple, arch, ImageKind, etc.) is compatible before checking the inner
+  /// image. Returns true if metadata is compatible, false otherwise.
+  virtual Expected<bool> isMetadataCompatible(
+      const OffloadBinMetadataTy &Metadata) const {
+    return true;
+  }
+
   virtual Error flushQueueImpl(omp_interop_val_t *Interop) {
     return Plugin::success();
   }
diff --git a/offload/plugins-nextgen/common/src/PluginInterface.cpp b/offload/plugins-nextgen/common/src/PluginInterface.cpp
index 7965ecbc98a78..7e441b974e026 100644
--- a/offload/plugins-nextgen/common/src/PluginInterface.cpp
+++ b/offload/plugins-nextgen/common/src/PluginInterface.cpp
@@ -1774,6 +1774,13 @@ int32_t GenericPluginTy::isPluginCompatible(StringRef Image) {
     auto &InnerImages = *ParsedOrErr;
 
     for (auto &[Metadata, InnerImage] : InnerImages) {
+      // First check if metadata is compatible
+      auto MetadataMatchOrErr = isMetadataCompatible(Metadata);
+      if (Error Err = MetadataMatchOrErr.takeError())
+        return HandleError(std::move(Err));
+      if (!*MetadataMatchOrErr)
+        continue;  
+
       if (isPluginCompatible(InnerImage))
         return true;
     }
@@ -1828,6 +1835,14 @@ int32_t GenericPluginTy::isDeviceCompatible(int32_t DeviceId, StringRef Image) {
     auto &InnerImages = *ParsedOrErr;
 
     for (auto &[Metadata, InnerImage] : InnerImages) {
+      // First check if metadata is compatible
+      auto MetadataMatchOrErr = isMetadataCompatible(Metadata);
+      if (Error Err = MetadataMatchOrErr.takeError())
+        return HandleError(std::move(Err));
+      if (!*MetadataMatchOrErr)
+        continue;  // Metadata not compatible, skip this image
+
+      // Metadata compatible, check inner image recursively
       if (isDeviceCompatible(DeviceId, InnerImage))
         return true;
     }
diff --git a/offload/plugins-nextgen/level_zero/include/L0Plugin.h b/offload/plugins-nextgen/level_zero/include/L0Plugin.h
index 7ab3696a49af6..434e3d6f5e08b 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Plugin.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Plugin.h
@@ -109,6 +109,9 @@ class LevelZeroPluginTy final : public GenericPluginTy {
   Error asyncBarrierImpl(omp_interop_val_t *Interop) override;
 
   Expected<bool> isImageCompatible(StringRef Image) const override;
+
+  Expected<bool>
+  isMetadataCompatible(const OffloadBinMetadataTy &Metadata) const override;
 };
 
 } // namespace llvm::omp::target::plugin
diff --git a/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp b/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp
index eaf2dbd295dc2..3cae0ec0bfead 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp
@@ -168,6 +168,26 @@ Expected<bool> LevelZeroPluginTy::isImageCompatible(StringRef Image) const {
   return identify_magic(Image) == file_magic::spirv_object;
 }
 
+Expected<bool> LevelZeroPluginTy::isMetadataCompatible(
+    const OffloadBinMetadataTy &Metadata) const {
+
+  llvm::Triple Triple(Metadata.Triple);
+  if (!Triple.isSPIRV() || Triple.getVendor() != llvm::Triple::Intel) {
+    ODBG(OLDT_Init) << "Rejecting image: incompatible triple '"
+                    << Metadata.Triple << "' (expected spirv64-intel)";
+    return false;
+  }
+
+  llvm::object::ImageKind Kind = Metadata.ImageKind;
+  if (Kind != llvm::object::IMG_SPIRV && Kind != llvm::object::IMG_Object) {
+    ODBG(OLDT_Init) << "Rejecting image: incompatible ImageKind " << Kind
+                    << " (expected IMG_SPIRV=" << llvm::object::IMG_SPIRV
+                    << " or IMG_Object=" << llvm::object::IMG_Object << ")";
+    return false;
+  }
+  return true;
+}
+
 Error LevelZeroPluginTy::syncBarrierImpl(omp_interop_val_t *Interop) {
   if (!Interop) {
     return Plugin::error(ErrorCode::INVALID_ARGUMENT,

>From 44a2f18da6ce816927e8e7da15033ca049aa8823 Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Thu, 12 Mar 2026 06:02:22 -0700
Subject: [PATCH 5/6] format

---
 offload/plugins-nextgen/amdgpu/src/rtl.cpp    |  3 +-
 .../common/include/PluginInterface.h          |  4 +--
 .../common/src/PluginInterface.cpp            | 34 ++++++++++---------
 offload/plugins-nextgen/cuda/src/rtl.cpp      |  3 +-
 offload/plugins-nextgen/host/src/rtl.cpp      |  3 +-
 .../level_zero/include/L0Device.h             |  3 +-
 .../level_zero/include/L0Program.h            |  3 +-
 .../level_zero/src/L0Program.cpp              |  9 ++---
 8 files changed, 30 insertions(+), 32 deletions(-)

diff --git a/offload/plugins-nextgen/amdgpu/src/rtl.cpp b/offload/plugins-nextgen/amdgpu/src/rtl.cpp
index a5ca49694fa0f..332d356348acc 100644
--- a/offload/plugins-nextgen/amdgpu/src/rtl.cpp
+++ b/offload/plugins-nextgen/amdgpu/src/rtl.cpp
@@ -2383,8 +2383,7 @@ struct AMDGPUDeviceTy : public GenericDeviceTy, AMDGenericDeviceTy {
 
   /// Load the binary image into the device and allocate an image object.
   Expected<DeviceImageTy *>
-  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
-                 int32_t ImageId,
+  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage, int32_t ImageId,
                  const OffloadBinMetadataTy *Metadata) override {
     // Allocate and initialize the image object.
     AMDGPUDeviceImageTy *AMDImage = Plugin.allocate<AMDGPUDeviceImageTy>();
diff --git a/offload/plugins-nextgen/common/include/PluginInterface.h b/offload/plugins-nextgen/common/include/PluginInterface.h
index f44c0a4f9397d..46fbe1df97121 100644
--- a/offload/plugins-nextgen/common/include/PluginInterface.h
+++ b/offload/plugins-nextgen/common/include/PluginInterface.h
@@ -1428,8 +1428,8 @@ struct GenericPluginTy {
   /// This is called when unwrapping OffloadBinary to check if the metadata
   /// (triple, arch, ImageKind, etc.) is compatible before checking the inner
   /// image. Returns true if metadata is compatible, false otherwise.
-  virtual Expected<bool> isMetadataCompatible(
-      const OffloadBinMetadataTy &Metadata) const {
+  virtual Expected<bool>
+  isMetadataCompatible(const OffloadBinMetadataTy &Metadata) const {
     return true;
   }
 
diff --git a/offload/plugins-nextgen/common/src/PluginInterface.cpp b/offload/plugins-nextgen/common/src/PluginInterface.cpp
index de0daed763349..7393170afb2d0 100644
--- a/offload/plugins-nextgen/common/src/PluginInterface.cpp
+++ b/offload/plugins-nextgen/common/src/PluginInterface.cpp
@@ -974,7 +974,7 @@ Error GenericDeviceTy::deinit(GenericPluginTy &Plugin) {
 
 // Helper function to finalize image loading after loadBinaryImpl succeeds
 static Error finalizeImageLoad(GenericDeviceTy &Device, GenericPluginTy &Plugin,
-                                DeviceImageTy *Image) {
+                               DeviceImageTy *Image) {
   Device.LoadedImages.push_back(Image);
 
   if (auto Err = Device.setupRPCServer(Plugin, *Image))
@@ -1001,8 +1001,7 @@ Expected<DeviceImageTy *> GenericDeviceTy::loadBinary(GenericPluginTy &Plugin,
                            "failure to jit IR image");
     }
     Buffer = std::move(*CompiledImageOrErr);
-  }
-  else if (identify_magic(InputTgtImage) == file_magic::offload_binary) {
+  } else if (identify_magic(InputTgtImage) == file_magic::offload_binary) {
     MemoryBufferRef InputBuffer(InputTgtImage, "offload_binary");
 
     auto ParsedOrErr = parseOffloadBinary(InputBuffer);
@@ -1021,20 +1020,21 @@ Expected<DeviceImageTy *> GenericDeviceTy::loadBinary(GenericPluginTy &Plugin,
           if (!FirstLoadedImage)
             FirstLoadedImage = *ImageOrErr;
         } else {
-          LoadErrors = joinErrors(std::move(LoadErrors), ImageOrErr.takeError());
+          LoadErrors =
+              joinErrors(std::move(LoadErrors), ImageOrErr.takeError());
         }
         continue;
       }
 
       int32_t Compatible = Plugin.isDeviceCompatible(DeviceId, InnerImage);
       if (!Compatible)
-        continue;  // Not compatible, skip
+        continue; // Not compatible, skip
 
       auto InnerBuffer = MemoryBuffer::getMemBufferCopy(InnerImage);
       const OffloadBinMetadataTy *MetadataPtr = &ExtractedMetadata;
 
-      auto ImageOrErr = loadBinaryImpl(std::move(InnerBuffer), LoadedImages.size(),
-                                        MetadataPtr);
+      auto ImageOrErr = loadBinaryImpl(std::move(InnerBuffer),
+                                       LoadedImages.size(), MetadataPtr);
       if (ImageOrErr) {
         DeviceImageTy *LoadedImage = *ImageOrErr;
 
@@ -1050,7 +1050,8 @@ Expected<DeviceImageTy *> GenericDeviceTy::loadBinary(GenericPluginTy &Plugin,
               device_load, Plugin.getUserId(DeviceId),
               /*FileName=*/nullptr, /*FileOffset=*/0, /*VmaInFile=*/nullptr,
               /*ImgSize=*/Bytes,
-              /*HostAddr=*/const_cast<unsigned char *>(InnerImage.bytes_begin()),
+              /*HostAddr=*/
+              const_cast<unsigned char *>(InnerImage.bytes_begin()),
               /*DeviceAddr=*/nullptr, /* FIXME: ModuleId */ 0);
         }
 #endif
@@ -1072,13 +1073,12 @@ Expected<DeviceImageTy *> GenericDeviceTy::loadBinary(GenericPluginTy &Plugin,
     }
 
     return FirstLoadedImage;
-  }
-  else {
+  } else {
     Buffer = MemoryBuffer::getMemBufferCopy(InputTgtImage);
   }
 
-  auto ImageOrErr = loadBinaryImpl(std::move(Buffer), LoadedImages.size(),
-                                    nullptr);
+  auto ImageOrErr =
+      loadBinaryImpl(std::move(Buffer), LoadedImages.size(), nullptr);
   if (!ImageOrErr)
     return ImageOrErr.takeError();
   DeviceImageTy *Image = *ImageOrErr;
@@ -1832,7 +1832,8 @@ int32_t GenericPluginTy::isPluginCompatible(StringRef Image) {
   }
   case file_magic::offload_binary: {
     // Unwrap OffloadBinary and check if ANY inner image is compatible
-    auto ParsedOrErr = parseOffloadBinary(MemoryBufferRef(Image, "offload_binary"));
+    auto ParsedOrErr =
+        parseOffloadBinary(MemoryBufferRef(Image, "offload_binary"));
     if (Error Err = ParsedOrErr.takeError())
       return HandleError(std::move(Err));
 
@@ -1844,7 +1845,7 @@ int32_t GenericPluginTy::isPluginCompatible(StringRef Image) {
       if (Error Err = MetadataMatchOrErr.takeError())
         return HandleError(std::move(Err));
       if (!*MetadataMatchOrErr)
-        continue;  
+        continue;
 
       if (isPluginCompatible(InnerImage))
         return true;
@@ -1893,7 +1894,8 @@ int32_t GenericPluginTy::isDeviceCompatible(int32_t DeviceId, StringRef Image) {
     return *MatchOrErr;
   }
   case file_magic::offload_binary: {
-    auto ParsedOrErr = parseOffloadBinary(MemoryBufferRef(Image, "offload_binary"));
+    auto ParsedOrErr =
+        parseOffloadBinary(MemoryBufferRef(Image, "offload_binary"));
     if (Error Err = ParsedOrErr.takeError())
       return HandleError(std::move(Err));
 
@@ -1905,7 +1907,7 @@ int32_t GenericPluginTy::isDeviceCompatible(int32_t DeviceId, StringRef Image) {
       if (Error Err = MetadataMatchOrErr.takeError())
         return HandleError(std::move(Err));
       if (!*MetadataMatchOrErr)
-        continue;  // Metadata not compatible, skip this image
+        continue; // Metadata not compatible, skip this image
 
       // Metadata compatible, check inner image recursively
       if (isDeviceCompatible(DeviceId, InnerImage))
diff --git a/offload/plugins-nextgen/cuda/src/rtl.cpp b/offload/plugins-nextgen/cuda/src/rtl.cpp
index ec99bbee97adc..771bca3df6c91 100644
--- a/offload/plugins-nextgen/cuda/src/rtl.cpp
+++ b/offload/plugins-nextgen/cuda/src/rtl.cpp
@@ -589,8 +589,7 @@ struct CUDADeviceTy : public GenericDeviceTy {
 
   /// Load the binary image into the device and allocate an image object.
   Expected<DeviceImageTy *>
-  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
-                 int32_t ImageId,
+  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage, int32_t ImageId,
                  const OffloadBinMetadataTy *Metadata) override {
     if (auto Err = setContext())
       return std::move(Err);
diff --git a/offload/plugins-nextgen/host/src/rtl.cpp b/offload/plugins-nextgen/host/src/rtl.cpp
index 65de2875c8582..a444aac904a57 100644
--- a/offload/plugins-nextgen/host/src/rtl.cpp
+++ b/offload/plugins-nextgen/host/src/rtl.cpp
@@ -200,8 +200,7 @@ struct GenELF64DeviceTy : public GenericDeviceTy {
 
   /// Load the binary image into the device and allocate an image object.
   Expected<DeviceImageTy *>
-  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
-                 int32_t ImageId,
+  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage, int32_t ImageId,
                  const OffloadBinMetadataTy *Metadata) override {
     // Allocate and initialize the image object.
     GenELF64DeviceImageTy *Image = Plugin.allocate<GenELF64DeviceImageTy>();
diff --git a/offload/plugins-nextgen/level_zero/include/L0Device.h b/offload/plugins-nextgen/level_zero/include/L0Device.h
index f72ca28b414f9..f37ca1b41371a 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Device.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Device.h
@@ -549,8 +549,7 @@ class L0DeviceTy final : public GenericDeviceTy {
 
   // Generic device interface implementation.
   Expected<DeviceImageTy *>
-  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
-                 int32_t ImageId,
+  loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage, int32_t ImageId,
                  const OffloadBinMetadataTy *Metadata) override;
   Error unloadBinaryImpl(DeviceImageTy *Image) override;
   Expected<void *> allocate(size_t Size, void *HstPtr,
diff --git a/offload/plugins-nextgen/level_zero/include/L0Program.h b/offload/plugins-nextgen/level_zero/include/L0Program.h
index 3cac93d5138c1..948f1f7bf9568 100644
--- a/offload/plugins-nextgen/level_zero/include/L0Program.h
+++ b/offload/plugins-nextgen/level_zero/include/L0Program.h
@@ -42,8 +42,7 @@ class L0ProgramBuilderTy {
 
 public:
   L0ProgramBuilderTy(L0DeviceTy &Device, std::unique_ptr<MemoryBuffer> &&Image,
-                     int32_t ImageId,
-                     const OffloadBinMetadataTy *Metadata)
+                     int32_t ImageId, const OffloadBinMetadataTy *Metadata)
       : Device(Device), Image(std::move(Image)), Metadata(Metadata) {
     (void)ImageId; // Unused parameter
   }
diff --git a/offload/plugins-nextgen/level_zero/src/L0Program.cpp b/offload/plugins-nextgen/level_zero/src/L0Program.cpp
index 9831452cc3811..1c5fd3c214f78 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Program.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Program.cpp
@@ -243,12 +243,13 @@ Error L0ProgramBuilderTy::buildModules(const std::string_view BuildOptions) {
       ODBG(OLDT_Module) << "Loading native binary module";
       ModuleFormat = ZE_MODULE_FORMAT_NATIVE;
     } else {
-      return Plugin::error(ErrorCode::UNKNOWN,
-                           "Unsupported image kind %d in OffloadBinary metadata",
-                           static_cast<int>(ImageKind));
+      return Plugin::error(
+          ErrorCode::UNKNOWN,
+          "Unsupported image kind %d in OffloadBinary metadata",
+          static_cast<int>(ImageKind));
     }
 
-    const uint8_t *ImgBegin = (const uint8_t*)Image.getBufferStart();
+    const uint8_t *ImgBegin = (const uint8_t *)Image.getBufferStart();
     return addModule(Image.getBufferSize(), ImgBegin, Options, ModuleFormat);
   }
 

>From a899e835ac4b10bc3ec7f881d357349e608f9531 Mon Sep 17 00:00:00 2001
From: "Duran, Alex" <alejandro.duran at intel.com>
Date: Thu, 12 Mar 2026 06:18:18 -0700
Subject: [PATCH 6/6] Provide convinience getString function

---
 offload/plugins-nextgen/common/include/PluginInterface.h | 7 +++++++
 offload/plugins-nextgen/level_zero/src/L0Program.cpp     | 8 ++++----
 2 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/offload/plugins-nextgen/common/include/PluginInterface.h b/offload/plugins-nextgen/common/include/PluginInterface.h
index 46fbe1df97121..d964a9155b6db 100644
--- a/offload/plugins-nextgen/common/include/PluginInterface.h
+++ b/offload/plugins-nextgen/common/include/PluginInterface.h
@@ -322,6 +322,13 @@ struct OffloadBinMetadataTy {
   std::string Triple;
   std::string Arch;
   llvm::StringMap<std::string> StringData;
+
+  StringRef getString(StringRef Key) const {
+    auto It = StringData.find(Key);
+    if (It != StringData.end())
+      return It->second;
+    return StringRef();
+  }
 };
 
 /// Class wrapping a __tgt_device_image and its offload entry table on a
diff --git a/offload/plugins-nextgen/level_zero/src/L0Program.cpp b/offload/plugins-nextgen/level_zero/src/L0Program.cpp
index 1c5fd3c214f78..917e54af10242 100644
--- a/offload/plugins-nextgen/level_zero/src/L0Program.cpp
+++ b/offload/plugins-nextgen/level_zero/src/L0Program.cpp
@@ -219,17 +219,17 @@ Error L0ProgramBuilderTy::buildModules(const std::string_view BuildOptions) {
   // Use metadata passed during loading if available
   if (Metadata) {
     llvm::object::ImageKind ImageKind = Metadata->ImageKind;
-    std::string CompileOpts = Metadata->StringData.lookup("compile-opts");
-    std::string LinkOpts = Metadata->StringData.lookup("link-opts");
+    StringRef CompileOpts = Metadata->getString("compile-opts");
+    StringRef LinkOpts = Metadata->getString("link-opts");
 
     ODBG(OLDT_Module) << "Using OffloadBinary metadata: kind=" << ImageKind;
 
     std::string Options(BuildOptions);
     if (!CompileOpts.empty() || !LinkOpts.empty()) {
       if (!CompileOpts.empty())
-        Options += " " + CompileOpts;
+        Options += " " + CompileOpts.str();
       if (!LinkOpts.empty())
-        Options += " " + LinkOpts;
+        Options += " " + LinkOpts.str();
       replaceDriverOptsWithBackendOpts(l0Device, Options);
       ODBG(OLDT_Module) << "Using compile options: " << CompileOpts
                         << ", link options: " << LinkOpts;



More information about the llvm-commits mailing list