From 3ff332ad0ff07b5303ced6b8c2bf7442fcbfb8b7 Mon Sep 17 00:00:00 2001 From: Alex Duran Date: Wed, 11 Mar 2026 11:42:36 +0100 Subject: [PATCH] [Offload][L0] Add support for OffloadBinary format in L0 plugin (#185404) - Accept OffloadBinaries as valid images by plugins that support them in the PluginInterface. - Add support in L0 plugin to extract SPIRV images and their associated metadata from an OffloadBinary image. Depends on: - #185663 Follow-up PRs: - #185413 (Changes SPIRV wrapper generation to use OffloadBinary) - #185425 (Adjusts llvm-objdump) - #184774 (Adjusts llvm-offload-binary) --- .../level_zero/src/L0Plugin.cpp | 38 ++++++++++ .../level_zero/src/L0Program.cpp | 71 ++++++++++++++++++- 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp b/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp index 2b3e61256639..fd8f36c28186 100644 --- a/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp +++ b/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp @@ -18,6 +18,8 @@ #include "L0Plugin.h" #include "L0Trace.h" +#include "llvm/Object/OffloadBinary.h" + namespace llvm::omp::target::plugin { using namespace llvm::omp::target; @@ -161,6 +163,42 @@ Expected LevelZeroPluginTy::isELFCompatible(uint32_t DeviceId, return isValidOneOmpImage(Image, MajorVer, MinorVer); } +// We only need to check for formats other than ELF here. +Expected 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; + } +} + Error LevelZeroPluginTy::syncBarrierImpl(omp_interop_val_t *Interop) { if (!Interop) { return Plugin::error(ErrorCode::INVALID_ARGUMENT, diff --git a/offload/plugins-nextgen/level_zero/src/L0Program.cpp b/offload/plugins-nextgen/level_zero/src/L0Program.cpp index 9c914f0eb7ca..60a71c9ad65a 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"; + + // 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(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(); + if (!LinkOpts.empty()) + Options += " " + LinkOpts.str(); + 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", + static_cast(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"; const uint8_t *ImgBegin = reinterpret_cast(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"; // Iterate over the images and pick the first one that fits. uint64_t ImageCount = 0;