[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