https://github.com/adurang updated https://github.com/llvm/llvm-project/pull/184774
>From 85d9675c73d55b113755686591d7e5a687903373 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Thu, 5 Mar 2026 03:22:11 -0800 Subject: [PATCH 01/31] [llvm][tools] Add support to llvm-offload-binary to unbundle spirv embedded images --- .../docs/CommandGuide/llvm-offload-binary.rst | 113 ++++++++++++++- .../llvm-offload-binary.ll | 6 + .../llvm-offload-binary.cpp | 130 +++++++++++++++++- 3 files changed, 237 insertions(+), 12 deletions(-) diff --git a/llvm/docs/CommandGuide/llvm-offload-binary.rst b/llvm/docs/CommandGuide/llvm-offload-binary.rst index 960b12d8af286..a018c355e2410 100644 --- a/llvm/docs/CommandGuide/llvm-offload-binary.rst +++ b/llvm/docs/CommandGuide/llvm-offload-binary.rst @@ -15,7 +15,24 @@ DESCRIPTION files into a single binary container. The resulting binary can then be embedded into the host section table to form a fat binary containing offloading code for different targets. Conversely, it can also extract previously bundled device -images. +images from offload binaries. + +**Extraction modes:** + +- **Extract all images**: When no ``--image`` filters are specified, all offload + images are automatically extracted with descriptive filenames. +- **Extract filtered images**: When ``--image`` filters are specified, only + matching images are extracted. + +**SPIR-V support:** + +For Intel SPIR-V targets (``spirv64-intel`` and ``spirv32-intel``), the tool +provides automatic extraction: + +- **Raw SPIR-V**: Detected using file magic and extracted with ``.spv`` extension +- **ELF-wrapped SPIR-V**: Extracts both the ELF wrapper (``.elf``) and embedded + SPIR-V binaries from sections named ``__openmp_offload_spirv_*`` (``.spv``) +- **Unknown format**: Extracted with ``.bin`` extension for inspection The binary format begins with the magic bytes ``0x10FF10AD``, followed by a version and size. Each binary contains its own header, allowing tools to locate @@ -32,12 +49,24 @@ EXAMPLE $ llvm-offload-binary -o out.bin \ --image=file=input.o,triple=nvptx64,arch=sm_70 - # Extract a matching image from a fat binary: - $ llvm-offload-binary in.bin \ - --image=file=output.o,triple=nvptx64,arch=sm_70 + # Extract all offload images from an executable (no filters): + $ llvm-offload-binary in.bin + # Output: + # Extracted: in-nvptx64-nvidia-cuda-sm_70.0.bc + # Extracted (ELF wrapper): in-spirv64-intel.0.elf + # Extracted SPIR-V: in-spirv64-intel.0_0.spv - # Extract and archive images into a static library: - $ llvm-offload-binary in.bin --archive -o libdevice.a + # Extract only SPIR-V images using filters: + $ llvm-offload-binary in.bin --image=triple=spirv64-intel + # Output: + # Extracted (ELF wrapper): in-spirv64-intel.0.elf + # Extracted SPIR-V: in-spirv64-intel.0_0.spv + + # Extract filtered images to a specific file: + $ llvm-offload-binary in.bin --image=file=output.bc,arch=sm_70 + + # Extract filtered images to an archive: + $ llvm-offload-binary in.bin --image=file=output.a,triple=nvptx64 --archive OPTIONS ------- @@ -179,7 +208,77 @@ The enumerated values for ``image kind`` and ``offload kind`` are: | OFK_SYCL | 0x04 | The producer was SYCL | +------------+-------+---------------------------------------+ +COMMON WORKFLOWS +---------------- + +**Workflow 1: Explore Executable Contents** + +Extract all embedded offload images to see what's inside: + +.. code-block:: console + + $ clang++ -fopenmp -fopenmp-targets=nvptx64,spirv64-intel app.cpp -o myapp + $ llvm-offload-binary myapp + # Output: + # Extracted: myapp-nvptx64-nvidia-cuda-sm_70.0.bc + # Extracted (ELF wrapper): myapp-spirv64-intel.1.elf + # Extracted SPIR-V: myapp-spirv64-intel.1_0.spv + +**Workflow 2: Extract Specific Target** + +Extract only images for a specific target: + +.. code-block:: console + + $ llvm-offload-binary myapp --image=triple=spirv64-intel + # Output: + # Extracted (ELF wrapper): myapp-spirv64-intel.0.elf + # Extracted SPIR-V: myapp-spirv64-intel.0_0.spv + +**Workflow 3: Create Device Image Archive** + +Extract filtered images into a static archive: + +.. code-block:: console + + $ llvm-offload-binary myapp --image=file=nvptx.a,triple=nvptx64 --archive + $ ar t nvptx.a + # Shows extracted CUDA images + +**Workflow 4: Validate SPIR-V** + +Extract and validate SPIR-V binaries: + +.. code-block:: console + + $ llvm-offload-binary myapp --image=triple=spirv64-intel + $ spirv-val myapp-spirv64-intel.0_0.spv + $ spirv-dis myapp-spirv64-intel.0_0.spv -o kernel.spvasm + +**Workflow 5: Bundle Multiple Targets** + +Create a fat binary from multiple device images: + +.. code-block:: console + + $ clang++ -fopenmp -fopenmp-targets=nvptx64 --offload-device-only kernel.cpp -o kernel_nvptx.bc + $ clang++ -fopenmp -fopenmp-targets=spirv64-intel --offload-device-only kernel.cpp -o kernel_spirv.bc + $ llvm-offload-binary -o bundle.bin \ + --image=file=kernel_nvptx.bc,triple=nvptx64,arch=sm_70 \ + --image=file=kernel_spirv.bc,triple=spirv64-intel + +**Workflow 6: Extract and Rebundle** + +Extract images from one binary and rebundle with modifications: + +.. code-block:: console + + $ llvm-offload-binary old_app + $ llvm-offload-binary -o new_bundle.bin \ + --image=file=old_app-nvptx64-nvidia-cuda-sm_70.0.bc,triple=nvptx64,arch=sm_70 \ + --image=file=new_kernel.bc,triple=nvptx64,arch=sm_80 + SEE ALSO -------- -:manpage:`clang(1)`, :manpage:`llvm-objdump(1)` +:manpage:`clang(1)`, :manpage:`llvm-objdump(1)`, :manpage:`spirv-val(1)`, :manpage:`spirv-dis(1)` diff --git a/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll b/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll index df46ad3a0d38a..3b835c24cd50e 100644 --- a/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll +++ b/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll @@ -15,3 +15,9 @@ ; RUN: llvm-offload-binary -o %t3 --image=file=%s ; RUN: llvm-offload-binary %t3 --image=file=%t4 ; RUN: diff %s %t4 + +; Test extracting all images without specifying --image filters +; RUN: llvm-offload-binary %t +; RUN: ls llvm-offload-binary.*-x-y-z-abc.0.| FileCheck --check-prefix=EXTRACT %s + +; EXTRACT: llvm-offload-binary.{{.*}}-x-y-z-abc.0. diff --git a/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp b/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp index e22d13b946651..ceaa7e7f0b04b 100644 --- a/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp +++ b/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp @@ -16,6 +16,8 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/Object/ArchiveWriter.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ObjectFile.h" #include "llvm/Object/OffloadBinary.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileOutputBuffer.h" @@ -135,6 +137,116 @@ static Error bundleImages() { return Error::success(); } +// Extract SPIR-V binaries from an ELF image with triple "spirv64-intel" or +// "spirv32-intel". These ELF images contain SPIR-V binaries in sections named +// "__openmp_offload_spirv_*". +static Expected<SmallVector<StringRef>> +extractSPIRVFromELF(StringRef ImageData) { + SmallVector<StringRef> SPIRVBinaries; + + // Try to parse as ELF object file + Expected<std::unique_ptr<ObjectFile>> ObjOrErr = + ObjectFile::createObjectFile(MemoryBufferRef(ImageData, "spirv-elf")); + if (!ObjOrErr) + return ObjOrErr.takeError(); + + ObjectFile &Obj = *ObjOrErr->get(); + if (!Obj.isELF()) + return createStringError(inconvertibleErrorCode(), + "Expected ELF format for Intel SPIR-V image"); + + // Extract all sections with name matching "__openmp_offload_spirv_*" + for (const SectionRef &Sec : Obj.sections()) { + Expected<StringRef> NameOrErr = Sec.getName(); + if (!NameOrErr) + continue; + + if (!NameOrErr->starts_with("__openmp_offload_spirv_")) + continue; + + Expected<StringRef> ContentsOrErr = Sec.getContents(); + if (!ContentsOrErr) + return ContentsOrErr.takeError(); + + SPIRVBinaries.push_back(*ContentsOrErr); + } + + if (SPIRVBinaries.empty()) + return createStringError(inconvertibleErrorCode(), + "No SPIR-V sections found in ELF image"); + + return SPIRVBinaries; +} + +// Helper function to extract a single binary image, with SPIR-V support. +// Returns Error on failure. +static Error extractBinary(const OffloadBinary *Binary, StringRef InputFile, + uint64_t Idx, StringSaver &Saver) { + // Check if this is a SPIR-V image that needs special handling + if (Binary->getTriple().starts_with("spirv64-intel")) { + StringRef ImageData = Binary->getImage(); + std::string BaseFilename = + sys::path::stem(InputFile).str() + "-" + Binary->getTriple().str(); + StringRef Arch = Binary->getArch(); + if (!Arch.empty()) + BaseFilename += "-" + Arch.str(); + BaseFilename += "." + std::to_string(Idx); + + // Check if the image is already raw SPIR-V (not ELF-wrapped) + if (identify_magic(ImageData) == file_magic::spirv_object) { + // Image is already SPIR-V, just extract it with .spv extension + StringRef Filename = Saver.save(BaseFilename + ".spv"); + if (Error E = writeFile(Filename, ImageData)) + return E; + outs() << "Extracted SPIR-V: " << Filename << "\n"; + return Error::success(); + } + + // Try to parse as ELF and extract SPIR-V from sections + auto SPIRVBinariesOrErr = extractSPIRVFromELF(ImageData); + if (!SPIRVBinariesOrErr) { + // Not ELF or no SPIR-V sections, extract as-is with .bin extension + StringRef Filename = Saver.save(BaseFilename + ".bin"); + if (Error E = writeFile(Filename, ImageData)) + return E; + outs() << "Extracted (unknown format): " << Filename << "\n"; + return Error::success(); + } + + // Successfully extracted SPIR-V from ELF + // Extract the ELF wrapper + StringRef ELFFilename = Saver.save(BaseFilename + ".elf"); + if (Error E = writeFile(ELFFilename, ImageData)) + return E; + outs() << "Extracted (ELF wrapper): " << ELFFilename << "\n"; + + // Extract each SPIR-V binary found in the ELF + uint64_t SPIRVIdx = 0; + for (StringRef SPIRVBinary : *SPIRVBinariesOrErr) { + StringRef Filename = + Saver.save(BaseFilename + "_" + std::to_string(SPIRVIdx++) + ".spv"); + if (Error E = writeFile(Filename, SPIRVBinary)) + return E; + outs() << "Extracted SPIR-V: " << Filename << "\n"; + } + } else { + // Regular extraction (non-SPIR-V) + std::string Filename = + sys::path::stem(InputFile).str() + "-" + Binary->getTriple().str(); + StringRef Arch = Binary->getArch(); + if (!Arch.empty()) + Filename += "-" + Arch.str(); + Filename += "." + std::to_string(Idx) + "." + + getImageKindName(Binary->getImageKind()).str(); + + if (Error E = writeFile(Saver.save(Filename), Binary->getImage())) + return E; + outs() << "Extracted: " << Filename << "\n"; + } + + return Error::success(); +} + static Error unbundleImages() { ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = MemoryBuffer::getFileOrSTDIN(InputFile); @@ -152,6 +264,18 @@ static Error unbundleImages() { if (Error Err = extractOffloadBinaries(*Buffer, Binaries)) return Err; + // If no filters specified, extract all images + if (DeviceImages.empty()) { + BumpPtrAllocator Alloc; + StringSaver Saver(Alloc); + uint64_t Idx = 0; + for (const OffloadFile &File : Binaries) { + if (Error E = extractBinary(File.getBinary(), InputFile, Idx++, Saver)) + return E; + } + return Error::success(); + } + // Try to extract each device image specified by the user from the input file. for (StringRef Image : DeviceImages) { BumpPtrAllocator Alloc; @@ -202,11 +326,7 @@ static Error unbundleImages() { } else { uint64_t Idx = 0; for (const OffloadBinary *Binary : Extracted) { - StringRef Filename = - Saver.save(sys::path::stem(InputFile) + "-" + Binary->getTriple() + - "-" + Binary->getArch() + "." + std::to_string(Idx++) + - "." + getImageKindName(Binary->getImageKind())); - if (Error E = writeFile(Filename, Binary->getImage())) + if (Error E = extractBinary(Binary, InputFile, Idx++, Saver)) return E; } } >From 5812ceee44483c794af8dfb9753272fa1a1e3e8c Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Thu, 5 Mar 2026 04:32:10 -0800 Subject: [PATCH 02/31] sigthly modify test --- llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll b/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll index 3b835c24cd50e..525745be9ce52 100644 --- a/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll +++ b/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll @@ -17,7 +17,6 @@ ; RUN: diff %s %t4 ; Test extracting all images without specifying --image filters -; RUN: llvm-offload-binary %t -; RUN: ls llvm-offload-binary.*-x-y-z-abc.0.| FileCheck --check-prefix=EXTRACT %s +; RUN: llvm-offload-binary %t | FileCheck --check-prefix=EXTRACT %s -; EXTRACT: llvm-offload-binary.{{.*}}-x-y-z-abc.0. +; EXTRACT: Extracted: llvm-offload-binary.{{.*}}-x-y-z-abc.0. >From 05fb3a59f42da4ab220a90d918145a4b321dd3b1 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Thu, 5 Mar 2026 14:47:35 -0800 Subject: [PATCH 03/31] consume error --- llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp b/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp index ceaa7e7f0b04b..8f94ee5e5be20 100644 --- a/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp +++ b/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp @@ -158,8 +158,11 @@ extractSPIRVFromELF(StringRef ImageData) { // Extract all sections with name matching "__openmp_offload_spirv_*" for (const SectionRef &Sec : Obj.sections()) { Expected<StringRef> NameOrErr = Sec.getName(); - if (!NameOrErr) + if (!NameOrErr) { + // consume error and skip this section + consumeError(NameOrErr.takeError()); continue; + } if (!NameOrErr->starts_with("__openmp_offload_spirv_")) continue; >From 0cbafd367321c547ad673c66fabcacbca629e039 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Mon, 9 Mar 2026 06:05:50 -0700 Subject: [PATCH 04/31] [llvm][offload] Change Intel's SPIRV containerizetion from ELF to OffloadBinary --- .../clang-linker-wrapper-spirv-elf.cpp | 22 ---- .../llvm/Frontend/Offloading/Utility.h | 27 ++++- llvm/lib/Frontend/Offloading/Utility.cpp | 114 ++++++------------ 3 files changed, 59 insertions(+), 104 deletions(-) delete mode 100644 clang/test/Tooling/clang-linker-wrapper-spirv-elf.cpp diff --git a/clang/test/Tooling/clang-linker-wrapper-spirv-elf.cpp b/clang/test/Tooling/clang-linker-wrapper-spirv-elf.cpp deleted file mode 100644 index 8a7d36d36b025..0000000000000 --- a/clang/test/Tooling/clang-linker-wrapper-spirv-elf.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// Verify the ELF packaging of OpenMP SPIR-V device images. -// REQUIRES: system-linux -// REQUIRES: spirv-tools -// REQUIRES: spirv-registered-target -// RUN: mkdir -p %t_tmp -// RUN: cd %t_tmp -// RUN: %clangxx -fopenmp -fopenmp-targets=spirv64-intel -nogpulib -c -o %t_clang-linker-wrapper-spirv-elf.o %s -// RUN: not clang-linker-wrapper -o a.out %t_clang-linker-wrapper-spirv-elf.o --save-temps --linker-path=ld -// RUN: llvm-offload-binary --image=triple=spirv64-intel,kind=openmp,file=%t.elf %t_tmp/a.out.openmp.image.wrapper.o -// RUN: llvm-readelf -h %t.elf | FileCheck -check-prefix=CHECK-MACHINE %s -// RUN: llvm-readelf -t %t.elf | FileCheck -check-prefix=CHECK-SECTION %s -// RUN: llvm-readelf -n %t.elf | FileCheck -check-prefix=CHECK-NOTES %s - -// CHECK-MACHINE: Machine: Intel Graphics Technology - -// CHECK-SECTION: .note.inteloneompoffload -// CHECK-SECTION: __openmp_offload_spirv_0 - -// CHECK-NOTES-COUNT-3: INTELONEOMPOFFLOAD -int main(int argc, char** argv) { - return 0; -} diff --git a/llvm/include/llvm/Frontend/Offloading/Utility.h b/llvm/include/llvm/Frontend/Offloading/Utility.h index 23e6702beb476..0c2e5e7c860e3 100644 --- a/llvm/include/llvm/Frontend/Offloading/Utility.h +++ b/llvm/include/llvm/Frontend/Offloading/Utility.h @@ -158,10 +158,29 @@ LLVM_ABI Error getAMDGPUMetaDataFromImage( } // namespace amdgpu namespace intel { -/// Containerizes an offloading binary into the ELF binary format expected by -/// the Intel runtime offload plugin. -LLVM_ABI Error -containerizeOpenMPSPIRVImage(std::unique_ptr<MemoryBuffer> &Binary); +/// Containerizes an SPIR-V image into inner OffloadBinary format. +/// Creates a nested OffloadBinary structure where the inner binary contains +/// the raw SPIR-V and associated metadata (version, format, triple, etc.). +/// This inner OffloadBinary is then embedded in an outer OffloadBinary. +/// +/// \param Binary The SPIR-V binary to containerize +/// \param CompileOpts Optional compilation options +/// \param LinkOpts Optional linking options +LLVM_ABI Error containerizeSPIRVImage(std::unique_ptr<MemoryBuffer> &Binary, + object::OffloadKind Kind, + StringRef CompileOpts = "", + StringRef LinkOpts = ""); + +/// Containerizes OpenMP SPIR-V image into inner OffloadBinary format. +/// \param Binary The SPIR-V binary to containerize +/// \param CompileOpts Optional compilation options +/// \param LinkOpts Optional linking options +inline LLVM_ABI Error containerizeOpenMPSPIRVImage( + std::unique_ptr<MemoryBuffer> &Binary, StringRef CompileOpts = "", + StringRef LinkOpts = "") { + return containerizeSPIRVImage(Binary, object::OffloadKind::OFK_OpenMP, + CompileOpts, LinkOpts); +} } // namespace intel } // namespace offloading } // namespace llvm diff --git a/llvm/lib/Frontend/Offloading/Utility.cpp b/llvm/lib/Frontend/Offloading/Utility.cpp index 5000488a52f37..d5571a15baf1c 100644 --- a/llvm/lib/Frontend/Offloading/Utility.cpp +++ b/llvm/lib/Frontend/Offloading/Utility.cpp @@ -15,6 +15,7 @@ #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Value.h" #include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/OffloadBinary.h" #include "llvm/ObjectYAML/ELFYAML.h" #include "llvm/ObjectYAML/yaml2obj.h" #include "llvm/Support/MemoryBufferRef.h" @@ -377,84 +378,41 @@ Error llvm::offloading::amdgpu::getAMDGPUMetaDataFromImage( } return Error::success(); } -Error offloading::intel::containerizeOpenMPSPIRVImage( - std::unique_ptr<MemoryBuffer> &Img) { - constexpr char INTEL_ONEOMP_OFFLOAD_VERSION[] = "1.0"; - constexpr int NT_INTEL_ONEOMP_OFFLOAD_VERSION = 1; - constexpr int NT_INTEL_ONEOMP_OFFLOAD_IMAGE_COUNT = 2; - constexpr int NT_INTEL_ONEOMP_OFFLOAD_IMAGE_AUX = 3; - - // Start creating notes for the ELF container. - std::vector<ELFYAML::NoteEntry> Notes; - std::string Version = toHex(INTEL_ONEOMP_OFFLOAD_VERSION); - Notes.emplace_back(ELFYAML::NoteEntry{"INTELONEOMPOFFLOAD", - yaml::BinaryRef(Version), - NT_INTEL_ONEOMP_OFFLOAD_VERSION}); - - // The AuxInfo string will hold auxiliary information for the image. - // ELFYAML::NoteEntry structures will hold references to the - // string, so we have to make sure the string is valid. - std::string AuxInfo; - - // TODO: Pass compile/link opts - StringRef CompileOpts = ""; - StringRef LinkOpts = ""; - - unsigned ImageFmt = 1; // SPIR-V format - - AuxInfo = toHex((Twine(0) + Twine('\0') + Twine(ImageFmt) + Twine('\0') + - CompileOpts + Twine('\0') + LinkOpts) - .str()); - Notes.emplace_back(ELFYAML::NoteEntry{"INTELONEOMPOFFLOAD", - yaml::BinaryRef(AuxInfo), - NT_INTEL_ONEOMP_OFFLOAD_IMAGE_AUX}); - - std::string ImgCount = toHex(Twine(1).str()); // always one image per ELF - Notes.emplace_back(ELFYAML::NoteEntry{"INTELONEOMPOFFLOAD", - yaml::BinaryRef(ImgCount), - NT_INTEL_ONEOMP_OFFLOAD_IMAGE_COUNT}); - - std::string YamlFile; - llvm::raw_string_ostream YamlFileStream(YamlFile); - - // Write the YAML template file. - - // We use 64-bit little-endian ELF currently. - ELFYAML::FileHeader Header{}; - Header.Class = ELF::ELFCLASS64; - Header.Data = ELF::ELFDATA2LSB; - Header.Type = ELF::ET_DYN; - Header.Machine = ELF::EM_INTELGT; - - // Create a section with notes. - ELFYAML::NoteSection Section{}; - Section.Type = ELF::SHT_NOTE; - Section.AddressAlign = 0; - Section.Name = ".note.inteloneompoffload"; - Section.Notes.emplace(std::move(Notes)); - - ELFYAML::Object Object{}; - Object.Header = Header; - Object.Chunks.push_back( - std::make_unique<ELFYAML::NoteSection>(std::move(Section))); - - // Create the section that will hold the image - ELFYAML::RawContentSection ImageSection{}; - ImageSection.Type = ELF::SHT_PROGBITS; - ImageSection.AddressAlign = 0; - std::string Name = "__openmp_offload_spirv_0"; - ImageSection.Name = Name; - ImageSection.Content = - llvm::yaml::BinaryRef(arrayRefFromStringRef(Img->getBuffer())); - Object.Chunks.push_back( - std::make_unique<ELFYAML::RawContentSection>(std::move(ImageSection))); - Error Err = Error::success(); - llvm::yaml::yaml2elf( - Object, YamlFileStream, - [&Err](const Twine &Msg) { Err = createStringError(Msg); }, UINT64_MAX); - if (Err) - return Err; +Error offloading::intel::containerizeSPIRVImage( + std::unique_ptr<MemoryBuffer> &Img, object::OffloadKind Kind, + StringRef CompileOpts, StringRef LinkOpts) { + using namespace object; + + // Create inner OffloadBinary containing the raw SPIR-V + OffloadBinary::OffloadingImage InnerImage; + InnerImage.TheImageKind = ImageKind::IMG_SPIRV; + InnerImage.TheOffloadKind = Kind; + InnerImage.Flags = 0; + + // Add metadata about the SPIR-V image as string key-value pairs. + MapVector<StringRef, StringRef> StringData; + StringData["version"] = "1.0"; + StringData["format"] = "spirv"; + StringData["triple"] = "spirv64-intel"; + + // Store compile/link options if provided + if (!CompileOpts.empty()) + StringData["compile-opts"] = CompileOpts; + if (!LinkOpts.empty()) + StringData["link-opts"] = LinkOpts; + + InnerImage.StringData = StringData; + + // Wrap the raw SPIR-V binary + InnerImage.Image = std::move(Img); + + // Serialize inner OffloadBinary + SmallVector<OffloadBinary::OffloadingImage> Images; + Images.push_back(std::move(InnerImage)); + SmallString<0> InnerBinaryData = OffloadBinary::write(Images); + + // Replace the buffer with the inner OffloadBinary + Img = MemoryBuffer::getMemBufferCopy(InnerBinaryData); - Img = MemoryBuffer::getMemBufferCopy(YamlFile); return Error::success(); } >From 30ed5e4f34339bbb7fd7e3c454f4fa109e1d893b Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Mon, 9 Mar 2026 07:09:28 -0700 Subject: [PATCH 05/31] [llvm][tools] Exted llvm-objdump to support nested OffloadBinaries --- llvm/tools/llvm-objdump/OffloadDump.cpp | 61 +++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/llvm/tools/llvm-objdump/OffloadDump.cpp b/llvm/tools/llvm-objdump/OffloadDump.cpp index cd2727069c2e9..1d48d44dcafc9 100644 --- a/llvm/tools/llvm-objdump/OffloadDump.cpp +++ b/llvm/tools/llvm-objdump/OffloadDump.cpp @@ -13,6 +13,7 @@ #include "OffloadDump.h" #include "llvm-objdump.h" +#include "llvm/BinaryFormat/Magic.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/OffloadBinary.h" #include "llvm/Object/OffloadBundle.h" @@ -43,13 +44,63 @@ static StringRef getImageName(const OffloadBinary &OB) { } } +static void printOffloadBinaryMetadata(const OffloadBinary &OB, + uint64_t justifaction) { + outs() << left_justify("kind", justifaction) << getImageName(OB) << "\n"; + outs() << left_justify("arch", justifaction) << OB.getArch() << "\n"; + outs() << left_justify("triple", justifaction) << OB.getTriple() << "\n"; + outs() << left_justify("producer", justifaction) + << getOffloadKindName(OB.getOffloadKind()) << "\n"; + + StringRef InnerImage = OB.getImage(); + outs() << left_justify(" image size", justifaction) << InnerImage.size() + << " bytes\n"; +} + +/// Print information about nested OffloadBinary (inner layer) +static void printNestedOffloadBinary(const OffloadBinary &OuterOB, + uint64_t Index) { + StringRef ImageData = OuterOB.getImage(); + + // Parse inner OffloadBinary + MemoryBufferRef InnerBuffer(ImageData, "inner-offload-binary"); + auto InnerBinariesOrErr = OffloadBinary::create(InnerBuffer); + if (!InnerBinariesOrErr) { + reportWarning("Failed to parse nested OffloadBinary: " + + toString(InnerBinariesOrErr.takeError()), + OuterOB.getFileName()); + return; + } + + auto &InnerBinaries = *InnerBinariesOrErr; + if (InnerBinaries.empty()) { + reportWarning("Nested OffloadBinary contains no entries", + OuterOB.getFileName()); + return; + } + + outs() << " [Nested OffloadBinary format detected]\n"; + outs() << " Number of inner images: " << InnerBinaries.size() << "\n"; + + // Display information for each inner image + for (uint64_t I = 0, E = InnerBinaries.size(); I != E; ++I) { + const OffloadBinary *InnerOB = InnerBinaries[I].get(); + + if (InnerBinaries.size() > 1) + outs() << " Inner image [" << I << "]:\n"; + + printOffloadBinaryMetadata(*InnerOB, 20); + } +} + static void printBinary(const OffloadBinary &OB, uint64_t Index) { outs() << "\nOFFLOADING IMAGE [" << Index << "]:\n"; - outs() << left_justify("kind", 16) << getImageName(OB) << "\n"; - outs() << left_justify("arch", 16) << OB.getArch() << "\n"; - outs() << left_justify("triple", 16) << OB.getTriple() << "\n"; - outs() << left_justify("producer", 16) - << getOffloadKindName(OB.getOffloadKind()) << "\n"; + printOffloadBinaryMetadata(OB, 16); + + StringRef ImageData = OB.getImage(); + // Check for nested OffloadBinary format + if (identify_magic(ImageData) == file_magic::offload_binary) + printNestedOffloadBinary(OB, Index); } /// Print the embedded offloading contents of an ObjectFile \p O. >From c16e95113ef5c183939e7cc89bcc438539487dfc Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Mon, 9 Mar 2026 07:29:09 -0700 Subject: [PATCH 06/31] Add test --- clang/test/Tooling/clang-linker-wrapper-spirv.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 clang/test/Tooling/clang-linker-wrapper-spirv.cpp diff --git a/clang/test/Tooling/clang-linker-wrapper-spirv.cpp b/clang/test/Tooling/clang-linker-wrapper-spirv.cpp new file mode 100644 index 0000000000000..74d1fb2c212c1 --- /dev/null +++ b/clang/test/Tooling/clang-linker-wrapper-spirv.cpp @@ -0,0 +1,14 @@ +// Verify the ELF packaging of OpenMP SPIR-V device images. +// REQUIRES: system-linux +// REQUIRES: spirv-tools +// REQUIRES: spirv-registered-target +// RUN: %clangxx -fopenmp -fopenmp-targets=spirv64-intel -nogpulib -o %t %s +// RUN: llvm-objdump --offloading %t | FileCheck -check-prefix=CHECK %s + +// CHECK: [Nested OffloadBinary +// CHECK: Number of inner images: 1 +// CHECK: spirv64-intel + +int main(int argc, char** argv) { + return 0; +} >From 7b5920b79b5fdd9ef152e245b03a13e78dda3c03 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Mon, 9 Mar 2026 07:40:58 -0700 Subject: [PATCH 07/31] fix tool output --- llvm/tools/llvm-objdump/OffloadDump.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/llvm/tools/llvm-objdump/OffloadDump.cpp b/llvm/tools/llvm-objdump/OffloadDump.cpp index 1d48d44dcafc9..50b4178495b68 100644 --- a/llvm/tools/llvm-objdump/OffloadDump.cpp +++ b/llvm/tools/llvm-objdump/OffloadDump.cpp @@ -45,15 +45,17 @@ static StringRef getImageName(const OffloadBinary &OB) { } static void printOffloadBinaryMetadata(const OffloadBinary &OB, - uint64_t justifaction) { - outs() << left_justify("kind", justifaction) << getImageName(OB) << "\n"; - outs() << left_justify("arch", justifaction) << OB.getArch() << "\n"; - outs() << left_justify("triple", justifaction) << OB.getTriple() << "\n"; - outs() << left_justify("producer", justifaction) + uint64_t level) { + const std::string Indent(level * 2, ' '); + + outs() << Indent << left_justify("kind", 16) << getImageName(OB) << "\n"; + outs() << Indent << left_justify("arch", 16) << OB.getArch() << "\n"; + outs() << Indent << left_justify("triple", 16) << OB.getTriple() << "\n"; + outs() << Indent << left_justify("producer", 16) << getOffloadKindName(OB.getOffloadKind()) << "\n"; StringRef InnerImage = OB.getImage(); - outs() << left_justify(" image size", justifaction) << InnerImage.size() + outs() << Indent << left_justify("image size", 16) << InnerImage.size() << " bytes\n"; } @@ -89,13 +91,13 @@ static void printNestedOffloadBinary(const OffloadBinary &OuterOB, if (InnerBinaries.size() > 1) outs() << " Inner image [" << I << "]:\n"; - printOffloadBinaryMetadata(*InnerOB, 20); + printOffloadBinaryMetadata(*InnerOB, 1); } } static void printBinary(const OffloadBinary &OB, uint64_t Index) { outs() << "\nOFFLOADING IMAGE [" << Index << "]:\n"; - printOffloadBinaryMetadata(OB, 16); + printOffloadBinaryMetadata(OB, 0); StringRef ImageData = OB.getImage(); // Check for nested OffloadBinary format >From 69ce34569bda1a251eedf75d5ca62f896a72145c Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Mon, 9 Mar 2026 07:42:00 -0700 Subject: [PATCH 08/31] fix messages --- llvm/tools/llvm-objdump/OffloadDump.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/tools/llvm-objdump/OffloadDump.cpp b/llvm/tools/llvm-objdump/OffloadDump.cpp index 50b4178495b68..ffe4d52d30446 100644 --- a/llvm/tools/llvm-objdump/OffloadDump.cpp +++ b/llvm/tools/llvm-objdump/OffloadDump.cpp @@ -68,7 +68,7 @@ static void printNestedOffloadBinary(const OffloadBinary &OuterOB, MemoryBufferRef InnerBuffer(ImageData, "inner-offload-binary"); auto InnerBinariesOrErr = OffloadBinary::create(InnerBuffer); if (!InnerBinariesOrErr) { - reportWarning("Failed to parse nested OffloadBinary: " + + reportWarning("failed to parse nested OffloadBinary: " + toString(InnerBinariesOrErr.takeError()), OuterOB.getFileName()); return; @@ -76,7 +76,7 @@ static void printNestedOffloadBinary(const OffloadBinary &OuterOB, auto &InnerBinaries = *InnerBinariesOrErr; if (InnerBinaries.empty()) { - reportWarning("Nested OffloadBinary contains no entries", + reportWarning("nested OffloadBinary contains no entries", OuterOB.getFileName()); return; } >From 7e831aba4670caee9350afa6e54c15df19b4fba8 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Mon, 9 Mar 2026 09:04:53 -0700 Subject: [PATCH 09/31] address reviews --- .../docs/CommandGuide/llvm-offload-binary.rst | 25 ++- .../llvm-offload-binary.ll | 2 +- .../llvm-offload-binary.cpp | 146 ++++++++++-------- 3 files changed, 88 insertions(+), 85 deletions(-) diff --git a/llvm/docs/CommandGuide/llvm-offload-binary.rst b/llvm/docs/CommandGuide/llvm-offload-binary.rst index a018c355e2410..cee0f77ba8980 100644 --- a/llvm/docs/CommandGuide/llvm-offload-binary.rst +++ b/llvm/docs/CommandGuide/llvm-offload-binary.rst @@ -17,22 +17,9 @@ into the host section table to form a fat binary containing offloading code for different targets. Conversely, it can also extract previously bundled device images from offload binaries. -**Extraction modes:** - -- **Extract all images**: When no ``--image`` filters are specified, all offload - images are automatically extracted with descriptive filenames. -- **Extract filtered images**: When ``--image`` filters are specified, only - matching images are extracted. - -**SPIR-V support:** - -For Intel SPIR-V targets (``spirv64-intel`` and ``spirv32-intel``), the tool -provides automatic extraction: - -- **Raw SPIR-V**: Detected using file magic and extracted with ``.spv`` extension -- **ELF-wrapped SPIR-V**: Extracts both the ELF wrapper (``.elf``) and embedded - SPIR-V binaries from sections named ``__openmp_offload_spirv_*`` (``.spv``) -- **Unknown format**: Extracted with ``.bin`` extension for inspection +When extracting images, if no :option:`--image` filters are specified, all +offload images are automatically extracted with descriptive filenames. When +:option:`--image` filters are provided, only matching images are extracted. The binary format begins with the magic bytes ``0x10FF10AD``, followed by a version and size. Each binary contains its own header, allowing tools to locate @@ -82,6 +69,12 @@ OPTIONS Commonly used optional keys include ``arch`` (e.g. ``sm_70`` for CUDA) and ``triple`` (e.g. nvptx64-nvidia-cuda). + When bundling, this option specifies images to include in the output binary. + When extracting, this option acts as a filter: only images matching the + specified keys are extracted. If no :option:`--image` options are provided + during extraction, all images are automatically extracted with descriptive + filenames. + .. option:: -o <file> Write output to <file>. When bundling, this specifies the fat binary filename. diff --git a/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll b/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll index 525745be9ce52..e410e033baa73 100644 --- a/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll +++ b/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll @@ -16,7 +16,7 @@ ; RUN: llvm-offload-binary %t3 --image=file=%t4 ; RUN: diff %s %t4 -; Test extracting all images without specifying --image filters +; Test extracting all images without specifying --image filters. ; RUN: llvm-offload-binary %t | FileCheck --check-prefix=EXTRACT %s ; EXTRACT: Extracted: llvm-offload-binary.{{.*}}-x-y-z-abc.0. diff --git a/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp b/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp index 8f94ee5e5be20..a1e09d34f2836 100644 --- a/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp +++ b/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp @@ -144,7 +144,7 @@ static Expected<SmallVector<StringRef>> extractSPIRVFromELF(StringRef ImageData) { SmallVector<StringRef> SPIRVBinaries; - // Try to parse as ELF object file + // Try to parse as ELF object file. Expected<std::unique_ptr<ObjectFile>> ObjOrErr = ObjectFile::createObjectFile(MemoryBufferRef(ImageData, "spirv-elf")); if (!ObjOrErr) @@ -152,14 +152,13 @@ extractSPIRVFromELF(StringRef ImageData) { ObjectFile &Obj = *ObjOrErr->get(); if (!Obj.isELF()) - return createStringError(inconvertibleErrorCode(), - "Expected ELF format for Intel SPIR-V image"); + return createStringError("expected ELF format for Intel SPIR-V image"); - // Extract all sections with name matching "__openmp_offload_spirv_*" + // Extract all sections with name matching "__openmp_offload_spirv_*". for (const SectionRef &Sec : Obj.sections()) { Expected<StringRef> NameOrErr = Sec.getName(); if (!NameOrErr) { - // consume error and skip this section + // Consume error and skip this section. consumeError(NameOrErr.takeError()); continue; } @@ -175,81 +174,92 @@ extractSPIRVFromELF(StringRef ImageData) { } if (SPIRVBinaries.empty()) - return createStringError(inconvertibleErrorCode(), - "No SPIR-V sections found in ELF image"); + return createStringError("no SPIR-V sections found in ELF image"); return SPIRVBinaries; } -// Helper function to extract a single binary image, with SPIR-V support. -// Returns Error on failure. -static Error extractBinary(const OffloadBinary *Binary, StringRef InputFile, - uint64_t Idx, StringSaver &Saver) { - // Check if this is a SPIR-V image that needs special handling - if (Binary->getTriple().starts_with("spirv64-intel")) { - StringRef ImageData = Binary->getImage(); - std::string BaseFilename = - sys::path::stem(InputFile).str() + "-" + Binary->getTriple().str(); - StringRef Arch = Binary->getArch(); - if (!Arch.empty()) - BaseFilename += "-" + Arch.str(); - BaseFilename += "." + std::to_string(Idx); - - // Check if the image is already raw SPIR-V (not ELF-wrapped) - if (identify_magic(ImageData) == file_magic::spirv_object) { - // Image is already SPIR-V, just extract it with .spv extension - StringRef Filename = Saver.save(BaseFilename + ".spv"); - if (Error E = writeFile(Filename, ImageData)) - return E; - outs() << "Extracted SPIR-V: " << Filename << "\n"; - return Error::success(); - } - - // Try to parse as ELF and extract SPIR-V from sections - auto SPIRVBinariesOrErr = extractSPIRVFromELF(ImageData); - if (!SPIRVBinariesOrErr) { - // Not ELF or no SPIR-V sections, extract as-is with .bin extension - StringRef Filename = Saver.save(BaseFilename + ".bin"); - if (Error E = writeFile(Filename, ImageData)) - return E; - outs() << "Extracted (unknown format): " << Filename << "\n"; - return Error::success(); - } +// Extract a SPIR-V binary image (spirv64-intel or spirv32-intel). +static Error extractSPIRVBinary(const OffloadBinary *Binary, + StringRef InputFile, uint64_t Idx, + StringSaver &Saver) { + StringRef ImageData = Binary->getImage(); + std::string BaseFilename = + sys::path::stem(InputFile).str() + "-" + Binary->getTriple().str(); + StringRef Arch = Binary->getArch(); + if (!Arch.empty()) + BaseFilename += "-" + Arch.str(); + BaseFilename += "." + std::to_string(Idx); + + // Check if the image is already raw SPIR-V (not ELF-wrapped). + if (identify_magic(ImageData) == file_magic::spirv_object) { + // Image is already SPIR-V, just extract it with .spv extension. + StringRef Filename = Saver.save(BaseFilename + ".spv"); + if (Error E = writeFile(Filename, ImageData)) + return E; + outs() << "Extracted SPIR-V: " << Filename << "\n"; + return Error::success(); + } - // Successfully extracted SPIR-V from ELF - // Extract the ELF wrapper - StringRef ELFFilename = Saver.save(BaseFilename + ".elf"); - if (Error E = writeFile(ELFFilename, ImageData)) + // Try to parse as ELF and extract SPIR-V from sections. + auto SPIRVBinariesOrErr = extractSPIRVFromELF(ImageData); + if (!SPIRVBinariesOrErr) { + // Not ELF or no SPIR-V sections, extract as-is with .bin extension. + StringRef Filename = Saver.save(BaseFilename + ".bin"); + if (Error E = writeFile(Filename, ImageData)) return E; - outs() << "Extracted (ELF wrapper): " << ELFFilename << "\n"; - - // Extract each SPIR-V binary found in the ELF - uint64_t SPIRVIdx = 0; - for (StringRef SPIRVBinary : *SPIRVBinariesOrErr) { - StringRef Filename = - Saver.save(BaseFilename + "_" + std::to_string(SPIRVIdx++) + ".spv"); - if (Error E = writeFile(Filename, SPIRVBinary)) - return E; - outs() << "Extracted SPIR-V: " << Filename << "\n"; - } - } else { - // Regular extraction (non-SPIR-V) - std::string Filename = - sys::path::stem(InputFile).str() + "-" + Binary->getTriple().str(); - StringRef Arch = Binary->getArch(); - if (!Arch.empty()) - Filename += "-" + Arch.str(); - Filename += "." + std::to_string(Idx) + "." + - getImageKindName(Binary->getImageKind()).str(); - - if (Error E = writeFile(Saver.save(Filename), Binary->getImage())) + outs() << "Extracted (unknown format): " << Filename << "\n"; + return Error::success(); + } + + // Successfully extracted SPIR-V from ELF. + // Extract the ELF wrapper. + StringRef ELFFilename = Saver.save(BaseFilename + ".elf"); + if (Error E = writeFile(ELFFilename, ImageData)) + return E; + outs() << "Extracted (ELF wrapper): " << ELFFilename << "\n"; + + // Extract each SPIR-V binary found in the ELF. + uint64_t SPIRVIdx = 0; + for (StringRef SPIRVBinary : *SPIRVBinariesOrErr) { + StringRef Filename = + Saver.save(BaseFilename + "_" + std::to_string(SPIRVIdx++) + ".spv"); + if (Error E = writeFile(Filename, SPIRVBinary)) return E; - outs() << "Extracted: " << Filename << "\n"; + outs() << "Extracted SPIR-V: " << Filename << "\n"; } return Error::success(); } +// Extract a regular (non-SPIR-V) binary image. +static Error extractRegularBinary(const OffloadBinary *Binary, + StringRef InputFile, uint64_t Idx, + StringSaver &Saver) { + std::string Filename = + sys::path::stem(InputFile).str() + "-" + Binary->getTriple().str(); + StringRef Arch = Binary->getArch(); + if (!Arch.empty()) + Filename += "-" + Arch.str(); + Filename += "." + std::to_string(Idx) + "." + + getImageKindName(Binary->getImageKind()).str(); + + if (Error E = writeFile(Saver.save(Filename), Binary->getImage())) + return E; + outs() << "Extracted: " << Filename << "\n"; + return Error::success(); +} + +// Helper function to extract a single binary image, with SPIR-V support. +// Returns Error on failure. +static Error extractBinary(const OffloadBinary *Binary, StringRef InputFile, + uint64_t Idx, StringSaver &Saver) { + // Check if this is a SPIR-V image that needs special handling. + if (Binary->getTriple().starts_with("spirv64-intel")) + return extractSPIRVBinary(Binary, InputFile, Idx, Saver); + return extractRegularBinary(Binary, InputFile, Idx, Saver); +} + static Error unbundleImages() { ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = MemoryBuffer::getFileOrSTDIN(InputFile); >From 95e00ba6c51d8957c4ac8cc068741bdaeea9530e Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Mon, 9 Mar 2026 10:13:56 -0700 Subject: [PATCH 10/31] support OffloadBinary instead of special ELF --- .../docs/CommandGuide/llvm-offload-binary.rst | 21 +-- .../llvm-offload-binary.cpp | 154 ++++++------------ 2 files changed, 59 insertions(+), 116 deletions(-) diff --git a/llvm/docs/CommandGuide/llvm-offload-binary.rst b/llvm/docs/CommandGuide/llvm-offload-binary.rst index cee0f77ba8980..87005967775db 100644 --- a/llvm/docs/CommandGuide/llvm-offload-binary.rst +++ b/llvm/docs/CommandGuide/llvm-offload-binary.rst @@ -21,6 +21,11 @@ When extracting images, if no :option:`--image` filters are specified, all offload images are automatically extracted with descriptive filenames. When :option:`--image` filters are provided, only matching images are extracted. +The tool supports nested OffloadBinary format, where device images can be wrapped +in an inner OffloadBinary container. When extracting, the tool automatically +detects and unwraps nested OffloadBinary images, making the format transparent +to users. + The binary format begins with the magic bytes ``0x10FF10AD``, followed by a version and size. Each binary contains its own header, allowing tools to locate offloading sections even when merged by a linker. Each offload entry includes @@ -40,14 +45,12 @@ EXAMPLE $ llvm-offload-binary in.bin # Output: # Extracted: in-nvptx64-nvidia-cuda-sm_70.0.bc - # Extracted (ELF wrapper): in-spirv64-intel.0.elf - # Extracted SPIR-V: in-spirv64-intel.0_0.spv + # Extracted: in-spirv64-intel-unknown.0.spv # Extract only SPIR-V images using filters: $ llvm-offload-binary in.bin --image=triple=spirv64-intel # Output: - # Extracted (ELF wrapper): in-spirv64-intel.0.elf - # Extracted SPIR-V: in-spirv64-intel.0_0.spv + # Extracted: in-spirv64-intel-unknown.0.spv # Extract filtered images to a specific file: $ llvm-offload-binary in.bin --image=file=output.bc,arch=sm_70 @@ -214,8 +217,7 @@ Extract all embedded offload images to see what's inside: $ llvm-offload-binary myapp # Output: # Extracted: myapp-nvptx64-nvidia-cuda-sm_70.0.bc - # Extracted (ELF wrapper): myapp-spirv64-intel.1.elf - # Extracted SPIR-V: myapp-spirv64-intel.1_0.spv + # Extracted: myapp-spirv64-intel-unknown.1.spv **Workflow 2: Extract Specific Target** @@ -225,8 +227,7 @@ Extract only images for a specific target: $ llvm-offload-binary myapp --image=triple=spirv64-intel # Output: - # Extracted (ELF wrapper): myapp-spirv64-intel.0.elf - # Extracted SPIR-V: myapp-spirv64-intel.0_0.spv + # Extracted: myapp-spirv64-intel-unknown.0.spv **Workflow 3: Create Device Image Archive** @@ -245,8 +246,8 @@ Extract and validate SPIR-V binaries: .. code-block:: console $ llvm-offload-binary myapp --image=triple=spirv64-intel - $ spirv-val myapp-spirv64-intel.0_0.spv - $ spirv-dis myapp-spirv64-intel.0_0.spv -o kernel.spvasm + $ spirv-val myapp-spirv64-intel-unknown.0.spv + $ spirv-dis myapp-spirv64-intel-unknown.0.spv -o kernel.spvasm **Workflow 5: Bundle Multiple Targets** diff --git a/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp b/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp index a1e09d34f2836..8a06b5e2ce38f 100644 --- a/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp +++ b/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp @@ -137,129 +137,66 @@ static Error bundleImages() { return Error::success(); } -// Extract SPIR-V binaries from an ELF image with triple "spirv64-intel" or -// "spirv32-intel". These ELF images contain SPIR-V binaries in sections named -// "__openmp_offload_spirv_*". -static Expected<SmallVector<StringRef>> -extractSPIRVFromELF(StringRef ImageData) { - SmallVector<StringRef> SPIRVBinaries; - - // Try to parse as ELF object file. - Expected<std::unique_ptr<ObjectFile>> ObjOrErr = - ObjectFile::createObjectFile(MemoryBufferRef(ImageData, "spirv-elf")); - if (!ObjOrErr) - return ObjOrErr.takeError(); - - ObjectFile &Obj = *ObjOrErr->get(); - if (!Obj.isELF()) - return createStringError("expected ELF format for Intel SPIR-V image"); - - // Extract all sections with name matching "__openmp_offload_spirv_*". - for (const SectionRef &Sec : Obj.sections()) { - Expected<StringRef> NameOrErr = Sec.getName(); - if (!NameOrErr) { - // Consume error and skip this section. - consumeError(NameOrErr.takeError()); - continue; - } - - if (!NameOrErr->starts_with("__openmp_offload_spirv_")) - continue; - - Expected<StringRef> ContentsOrErr = Sec.getContents(); - if (!ContentsOrErr) - return ContentsOrErr.takeError(); - - SPIRVBinaries.push_back(*ContentsOrErr); +// Recursively unwrap nested OffloadBinaries to get the actual device image. +static Expected<StringRef> unwrapImage(StringRef ImageData) { + // Check if the image contains a nested OffloadBinary. + if (identify_magic(ImageData) == file_magic::offload_binary) { + // Parse nested OffloadBinary. + MemoryBufferRef InnerBuffer(ImageData, "nested-offload-binary"); + auto InnerBinaries = OffloadBinary::create(InnerBuffer); + if (!InnerBinaries) + return InnerBinaries.takeError(); + + // For single entry, recursively unwrap. + if (InnerBinaries->size() == 1) + return unwrapImage((*InnerBinaries)[0]->getImage()); + + // Multiple entries not supported for single file extraction. + return createStringError(inconvertibleErrorCode(), + "nested OffloadBinary contains multiple entries"); } - if (SPIRVBinaries.empty()) - return createStringError("no SPIR-V sections found in ELF image"); - - return SPIRVBinaries; + // Base case: return the actual device image. + return ImageData; } -// Extract a SPIR-V binary image (spirv64-intel or spirv32-intel). -static Error extractSPIRVBinary(const OffloadBinary *Binary, - StringRef InputFile, uint64_t Idx, - StringSaver &Saver) { +// Extract a single OffloadBinary, recursively handling nested OffloadBinaries. +static Error extractBinary(const OffloadBinary *Binary, StringRef InputFile, + uint64_t &Idx, StringSaver &Saver) { StringRef ImageData = Binary->getImage(); - std::string BaseFilename = - sys::path::stem(InputFile).str() + "-" + Binary->getTriple().str(); - StringRef Arch = Binary->getArch(); - if (!Arch.empty()) - BaseFilename += "-" + Arch.str(); - BaseFilename += "." + std::to_string(Idx); - - // Check if the image is already raw SPIR-V (not ELF-wrapped). - if (identify_magic(ImageData) == file_magic::spirv_object) { - // Image is already SPIR-V, just extract it with .spv extension. - StringRef Filename = Saver.save(BaseFilename + ".spv"); - if (Error E = writeFile(Filename, ImageData)) - return E; - outs() << "Extracted SPIR-V: " << Filename << "\n"; - return Error::success(); - } - // Try to parse as ELF and extract SPIR-V from sections. - auto SPIRVBinariesOrErr = extractSPIRVFromELF(ImageData); - if (!SPIRVBinariesOrErr) { - // Not ELF or no SPIR-V sections, extract as-is with .bin extension. - StringRef Filename = Saver.save(BaseFilename + ".bin"); - if (Error E = writeFile(Filename, ImageData)) - return E; - outs() << "Extracted (unknown format): " << Filename << "\n"; + // Check if the image contains a nested OffloadBinary. + if (identify_magic(ImageData) == file_magic::offload_binary) { + // Parse nested OffloadBinary. + MemoryBufferRef InnerBuffer(ImageData, "nested-offload-binary"); + auto InnerBinaries = OffloadBinary::create(InnerBuffer); + if (!InnerBinaries) + return InnerBinaries.takeError(); + + // Recursively extract each nested binary. + for (const auto &InnerBinary : *InnerBinaries) { + if (Error E = extractBinary(InnerBinary.get(), InputFile, Idx, Saver)) + return E; + } return Error::success(); } - // Successfully extracted SPIR-V from ELF. - // Extract the ELF wrapper. - StringRef ELFFilename = Saver.save(BaseFilename + ".elf"); - if (Error E = writeFile(ELFFilename, ImageData)) - return E; - outs() << "Extracted (ELF wrapper): " << ELFFilename << "\n"; - - // Extract each SPIR-V binary found in the ELF. - uint64_t SPIRVIdx = 0; - for (StringRef SPIRVBinary : *SPIRVBinariesOrErr) { - StringRef Filename = - Saver.save(BaseFilename + "_" + std::to_string(SPIRVIdx++) + ".spv"); - if (Error E = writeFile(Filename, SPIRVBinary)) - return E; - outs() << "Extracted SPIR-V: " << Filename << "\n"; - } - - return Error::success(); -} - -// Extract a regular (non-SPIR-V) binary image. -static Error extractRegularBinary(const OffloadBinary *Binary, - StringRef InputFile, uint64_t Idx, - StringSaver &Saver) { + // Base case: extract the actual device image. std::string Filename = sys::path::stem(InputFile).str() + "-" + Binary->getTriple().str(); StringRef Arch = Binary->getArch(); if (!Arch.empty()) Filename += "-" + Arch.str(); - Filename += "." + std::to_string(Idx) + "." + + Filename += "." + std::to_string(Idx++) + "." + getImageKindName(Binary->getImageKind()).str(); - if (Error E = writeFile(Saver.save(Filename), Binary->getImage())) + if (Error E = writeFile(Saver.save(Filename), ImageData)) return E; + outs() << "Extracted: " << Filename << "\n"; return Error::success(); } -// Helper function to extract a single binary image, with SPIR-V support. -// Returns Error on failure. -static Error extractBinary(const OffloadBinary *Binary, StringRef InputFile, - uint64_t Idx, StringSaver &Saver) { - // Check if this is a SPIR-V image that needs special handling. - if (Binary->getTriple().starts_with("spirv64-intel")) - return extractSPIRVBinary(Binary, InputFile, Idx, Saver); - return extractRegularBinary(Binary, InputFile, Idx, Saver); -} - static Error unbundleImages() { ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = MemoryBuffer::getFileOrSTDIN(InputFile); @@ -277,13 +214,13 @@ static Error unbundleImages() { if (Error Err = extractOffloadBinaries(*Buffer, Binaries)) return Err; - // If no filters specified, extract all images + // If no filters specified, extract all images. if (DeviceImages.empty()) { BumpPtrAllocator Alloc; StringSaver Saver(Alloc); uint64_t Idx = 0; for (const OffloadFile &File : Binaries) { - if (Error E = extractBinary(File.getBinary(), InputFile, Idx++, Saver)) + if (Error E = extractBinary(File.getBinary(), InputFile, Idx, Saver)) return E; } return Error::success(); @@ -334,12 +271,17 @@ static Error unbundleImages() { WithColor::warning(errs(), PackagerExecutable) << "Multiple inputs match to a single file, '" << It->second << "'\n"; - if (Error E = writeFile(It->second, Extracted.back()->getImage())) + const OffloadBinary *Binary = Extracted.back(); + // Recursively unwrap any nested OffloadBinaries. + auto ImageOrErr = unwrapImage(Binary->getImage()); + if (!ImageOrErr) + return ImageOrErr.takeError(); + if (Error E = writeFile(It->second, *ImageOrErr)) return E; } else { uint64_t Idx = 0; for (const OffloadBinary *Binary : Extracted) { - if (Error E = extractBinary(Binary, InputFile, Idx++, Saver)) + if (Error E = extractBinary(Binary, InputFile, Idx, Saver)) return E; } } >From 2b43d73bc6e492dc1b189e2f0ff251ac39c7ecc5 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Mon, 9 Mar 2026 14:35:10 -0700 Subject: [PATCH 11/31] generalize containerizeImage --- .../llvm/Frontend/Offloading/Utility.h | 32 ++++++------ llvm/lib/Frontend/Offloading/Utility.cpp | 49 +++++++++++-------- 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/llvm/include/llvm/Frontend/Offloading/Utility.h b/llvm/include/llvm/Frontend/Offloading/Utility.h index 0c2e5e7c860e3..58676ebbd14fc 100644 --- a/llvm/include/llvm/Frontend/Offloading/Utility.h +++ b/llvm/include/llvm/Frontend/Offloading/Utility.h @@ -157,30 +157,28 @@ LLVM_ABI Error getAMDGPUMetaDataFromImage( uint16_t &ELFABIVersion); } // namespace amdgpu -namespace intel { -/// Containerizes an SPIR-V image into inner OffloadBinary format. +/// Containerizes an image into inner OffloadBinary format. /// Creates a nested OffloadBinary structure where the inner binary contains -/// the raw SPIR-V and associated metadata (version, format, triple, etc.). -/// This inner OffloadBinary is then embedded in an outer OffloadBinary. -/// -/// \param Binary The SPIR-V binary to containerize -/// \param CompileOpts Optional compilation options -/// \param LinkOpts Optional linking options -LLVM_ABI Error containerizeSPIRVImage(std::unique_ptr<MemoryBuffer> &Binary, - object::OffloadKind Kind, - StringRef CompileOpts = "", - StringRef LinkOpts = ""); +/// the raw image and associated metadata (version, format, triple, etc.). +/// \param Binary The image to containerize +/// \param ImageKind The format of the image, e.g. SPIR-V or CUBIN. +/// \param OffloadKind The expected consumer of the image, e.g. CUDA or OpenMP. +/// \param ImageFlags Flags associated with the image, e.g. for AMDGPU the features +/// \param MetaData The key-value map of metadata to be associated with the image. +LLVM_ABI Error containerizeImage(std::unique_ptr<MemoryBuffer> &Binary, + object::ImageKind ImageKind, + object::OffloadKind OffloadKind, + int32_t ImageFlags, + MapVector<StringRef, StringRef> &MetaData); +namespace intel { /// Containerizes OpenMP SPIR-V image into inner OffloadBinary format. /// \param Binary The SPIR-V binary to containerize /// \param CompileOpts Optional compilation options /// \param LinkOpts Optional linking options -inline LLVM_ABI Error containerizeOpenMPSPIRVImage( +LLVM_ABI Error containerizeOpenMPSPIRVImage( std::unique_ptr<MemoryBuffer> &Binary, StringRef CompileOpts = "", - StringRef LinkOpts = "") { - return containerizeSPIRVImage(Binary, object::OffloadKind::OFK_OpenMP, - CompileOpts, LinkOpts); -} + StringRef LinkOpts = ""); } // namespace intel } // namespace offloading } // namespace llvm diff --git a/llvm/lib/Frontend/Offloading/Utility.cpp b/llvm/lib/Frontend/Offloading/Utility.cpp index d5571a15baf1c..a22b4d894696b 100644 --- a/llvm/lib/Frontend/Offloading/Utility.cpp +++ b/llvm/lib/Frontend/Offloading/Utility.cpp @@ -378,30 +378,22 @@ Error llvm::offloading::amdgpu::getAMDGPUMetaDataFromImage( } return Error::success(); } -Error offloading::intel::containerizeSPIRVImage( - std::unique_ptr<MemoryBuffer> &Img, object::OffloadKind Kind, - StringRef CompileOpts, StringRef LinkOpts) { + +Error offloading::containerizeImage(std::unique_ptr<MemoryBuffer> &Img, + object::ImageKind ImageKind, + object::OffloadKind OffloadKind, + int32_t ImageFlags, + MapVector<StringRef, StringRef> &MetaData) { using namespace object; - // Create inner OffloadBinary containing the raw SPIR-V + // Create inner OffloadBinary containing the raw image OffloadBinary::OffloadingImage InnerImage; - InnerImage.TheImageKind = ImageKind::IMG_SPIRV; - InnerImage.TheOffloadKind = Kind; - InnerImage.Flags = 0; - - // Add metadata about the SPIR-V image as string key-value pairs. - MapVector<StringRef, StringRef> StringData; - StringData["version"] = "1.0"; - StringData["format"] = "spirv"; - StringData["triple"] = "spirv64-intel"; - - // Store compile/link options if provided - if (!CompileOpts.empty()) - StringData["compile-opts"] = CompileOpts; - if (!LinkOpts.empty()) - StringData["link-opts"] = LinkOpts; + InnerImage.TheImageKind = ImageKind; + InnerImage.TheOffloadKind = OffloadKind; + InnerImage.Flags = ImageFlags; - InnerImage.StringData = StringData; + for (const auto &KV : MetaData) + InnerImage.StringData[KV.first] = KV.second; // Wrap the raw SPIR-V binary InnerImage.Image = std::move(Img); @@ -416,3 +408,20 @@ Error offloading::intel::containerizeSPIRVImage( return Error::success(); } + +Error offloading::intel::containerizeOpenMPSPIRVImage( + std::unique_ptr<MemoryBuffer> &Binary, StringRef CompileOpts, + StringRef LinkOpts) { + MapVector<StringRef, StringRef> MetaData; + MetaData["version"] = "1.0"; + MetaData["format"] = "spirv"; + MetaData["triple"] = "spirv64-openmp"; + if (!CompileOpts.empty()) + MetaData["compile-opts"] = CompileOpts; + if (!LinkOpts.empty()) + MetaData["link-opts"] = LinkOpts; + + return containerizeImage(Binary, object::ImageKind::IMG_SPIRV, + object::OffloadKind::OFK_OpenMP, /*ImageFlags=*/0, + MetaData); +} >From c9646a4e5436d23e439f142ee16896ea6db54f59 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Mon, 9 Mar 2026 14:50:31 -0700 Subject: [PATCH 12/31] format --- llvm/include/llvm/Frontend/Offloading/Utility.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/llvm/include/llvm/Frontend/Offloading/Utility.h b/llvm/include/llvm/Frontend/Offloading/Utility.h index 58676ebbd14fc..d8b1256874f7e 100644 --- a/llvm/include/llvm/Frontend/Offloading/Utility.h +++ b/llvm/include/llvm/Frontend/Offloading/Utility.h @@ -163,13 +163,15 @@ LLVM_ABI Error getAMDGPUMetaDataFromImage( /// \param Binary The image to containerize /// \param ImageKind The format of the image, e.g. SPIR-V or CUBIN. /// \param OffloadKind The expected consumer of the image, e.g. CUDA or OpenMP. -/// \param ImageFlags Flags associated with the image, e.g. for AMDGPU the features -/// \param MetaData The key-value map of metadata to be associated with the image. +/// \param ImageFlags Flags associated with the image, e.g. for AMDGPU the +/// features +/// \param MetaData The key-value map of metadata to be associated with the +/// image. LLVM_ABI Error containerizeImage(std::unique_ptr<MemoryBuffer> &Binary, - object::ImageKind ImageKind, - object::OffloadKind OffloadKind, - int32_t ImageFlags, - MapVector<StringRef, StringRef> &MetaData); + object::ImageKind ImageKind, + object::OffloadKind OffloadKind, + int32_t ImageFlags, + MapVector<StringRef, StringRef> &MetaData); namespace intel { /// Containerizes OpenMP SPIR-V image into inner OffloadBinary format. >From 2c6e20f0e391a701efe24604d5dad80348ec697b Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Mon, 9 Mar 2026 14:52:10 -0700 Subject: [PATCH 13/31] adjust comments --- llvm/include/llvm/Frontend/Offloading/Utility.h | 10 +++++----- llvm/lib/Frontend/Offloading/Utility.cpp | 6 +----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/llvm/include/llvm/Frontend/Offloading/Utility.h b/llvm/include/llvm/Frontend/Offloading/Utility.h index d8b1256874f7e..101a3ea73baf7 100644 --- a/llvm/include/llvm/Frontend/Offloading/Utility.h +++ b/llvm/include/llvm/Frontend/Offloading/Utility.h @@ -160,11 +160,11 @@ LLVM_ABI Error getAMDGPUMetaDataFromImage( /// Containerizes an image into inner OffloadBinary format. /// Creates a nested OffloadBinary structure where the inner binary contains /// the raw image and associated metadata (version, format, triple, etc.). -/// \param Binary The image to containerize +/// \param Binary The image to containerize. /// \param ImageKind The format of the image, e.g. SPIR-V or CUBIN. /// \param OffloadKind The expected consumer of the image, e.g. CUDA or OpenMP. /// \param ImageFlags Flags associated with the image, e.g. for AMDGPU the -/// features +/// features. /// \param MetaData The key-value map of metadata to be associated with the /// image. LLVM_ABI Error containerizeImage(std::unique_ptr<MemoryBuffer> &Binary, @@ -175,9 +175,9 @@ LLVM_ABI Error containerizeImage(std::unique_ptr<MemoryBuffer> &Binary, namespace intel { /// Containerizes OpenMP SPIR-V image into inner OffloadBinary format. -/// \param Binary The SPIR-V binary to containerize -/// \param CompileOpts Optional compilation options -/// \param LinkOpts Optional linking options +/// \param Binary The SPIR-V binary to containerize. +/// \param CompileOpts Optional compilation options. +/// \param LinkOpts Optional linking options. LLVM_ABI Error containerizeOpenMPSPIRVImage( std::unique_ptr<MemoryBuffer> &Binary, StringRef CompileOpts = "", StringRef LinkOpts = ""); diff --git a/llvm/lib/Frontend/Offloading/Utility.cpp b/llvm/lib/Frontend/Offloading/Utility.cpp index a22b4d894696b..de1d69ad0b1fc 100644 --- a/llvm/lib/Frontend/Offloading/Utility.cpp +++ b/llvm/lib/Frontend/Offloading/Utility.cpp @@ -386,7 +386,7 @@ Error offloading::containerizeImage(std::unique_ptr<MemoryBuffer> &Img, MapVector<StringRef, StringRef> &MetaData) { using namespace object; - // Create inner OffloadBinary containing the raw image + // Create inner OffloadBinary containing the raw image. OffloadBinary::OffloadingImage InnerImage; InnerImage.TheImageKind = ImageKind; InnerImage.TheOffloadKind = OffloadKind; @@ -395,17 +395,13 @@ Error offloading::containerizeImage(std::unique_ptr<MemoryBuffer> &Img, for (const auto &KV : MetaData) InnerImage.StringData[KV.first] = KV.second; - // Wrap the raw SPIR-V binary InnerImage.Image = std::move(Img); - // Serialize inner OffloadBinary SmallVector<OffloadBinary::OffloadingImage> Images; Images.push_back(std::move(InnerImage)); SmallString<0> InnerBinaryData = OffloadBinary::write(Images); - // Replace the buffer with the inner OffloadBinary Img = MemoryBuffer::getMemBufferCopy(InnerBinaryData); - return Error::success(); } >From d75f4ed889df40946b7c4f16d0bb54c8965887f2 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Mon, 9 Mar 2026 14:54:41 -0700 Subject: [PATCH 14/31] fix intel triple --- llvm/lib/Frontend/Offloading/Utility.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Frontend/Offloading/Utility.cpp b/llvm/lib/Frontend/Offloading/Utility.cpp index de1d69ad0b1fc..ef0a40638b444 100644 --- a/llvm/lib/Frontend/Offloading/Utility.cpp +++ b/llvm/lib/Frontend/Offloading/Utility.cpp @@ -411,7 +411,7 @@ Error offloading::intel::containerizeOpenMPSPIRVImage( MapVector<StringRef, StringRef> MetaData; MetaData["version"] = "1.0"; MetaData["format"] = "spirv"; - MetaData["triple"] = "spirv64-openmp"; + MetaData["triple"] = "spirv64-intel"; if (!CompileOpts.empty()) MetaData["compile-opts"] = CompileOpts; if (!LinkOpts.empty()) >From 1d2884ba72f513925464dfb7cf8af09d94c7c918 Mon Sep 17 00:00:00 2001 From: Alex Duran <[email protected]> Date: Tue, 10 Mar 2026 15:33:16 +0100 Subject: [PATCH 15/31] Update llvm/lib/Frontend/Offloading/Utility.cpp Co-authored-by: Yury Plyakhin <[email protected]> --- llvm/lib/Frontend/Offloading/Utility.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/llvm/lib/Frontend/Offloading/Utility.cpp b/llvm/lib/Frontend/Offloading/Utility.cpp index ef0a40638b444..ac5224e4ea718 100644 --- a/llvm/lib/Frontend/Offloading/Utility.cpp +++ b/llvm/lib/Frontend/Offloading/Utility.cpp @@ -397,9 +397,7 @@ Error offloading::containerizeImage(std::unique_ptr<MemoryBuffer> &Img, InnerImage.Image = std::move(Img); - SmallVector<OffloadBinary::OffloadingImage> Images; - Images.push_back(std::move(InnerImage)); - SmallString<0> InnerBinaryData = OffloadBinary::write(Images); + SmallString<0> InnerBinaryData = OffloadBinary::write(InnerImage); Img = MemoryBuffer::getMemBufferCopy(InnerBinaryData); return Error::success(); >From 039815b10364342a07473ce02eee8b4350f12a2c Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Tue, 10 Mar 2026 10:50:35 -0700 Subject: [PATCH 16/31] remove redundant format from metadata --- llvm/lib/Frontend/Offloading/Utility.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/llvm/lib/Frontend/Offloading/Utility.cpp b/llvm/lib/Frontend/Offloading/Utility.cpp index ef0a40638b444..48be331540ca3 100644 --- a/llvm/lib/Frontend/Offloading/Utility.cpp +++ b/llvm/lib/Frontend/Offloading/Utility.cpp @@ -410,7 +410,6 @@ Error offloading::intel::containerizeOpenMPSPIRVImage( StringRef LinkOpts) { MapVector<StringRef, StringRef> MetaData; MetaData["version"] = "1.0"; - MetaData["format"] = "spirv"; MetaData["triple"] = "spirv64-intel"; if (!CompileOpts.empty()) MetaData["compile-opts"] = CompileOpts; >From 61d9430a75319b8b9b78592cd680b5e8db70fb45 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Tue, 10 Mar 2026 11:21:30 -0700 Subject: [PATCH 17/31] address comments --- llvm/include/llvm/Frontend/Offloading/Utility.h | 7 ++++--- llvm/lib/Frontend/Offloading/Utility.cpp | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/llvm/include/llvm/Frontend/Offloading/Utility.h b/llvm/include/llvm/Frontend/Offloading/Utility.h index 101a3ea73baf7..656cdcf6b4d5d 100644 --- a/llvm/include/llvm/Frontend/Offloading/Utility.h +++ b/llvm/include/llvm/Frontend/Offloading/Utility.h @@ -157,12 +157,13 @@ LLVM_ABI Error getAMDGPUMetaDataFromImage( uint16_t &ELFABIVersion); } // namespace amdgpu -/// Containerizes an image into inner OffloadBinary format. +/// Containerizes an image within an OffloadBinary image. /// Creates a nested OffloadBinary structure where the inner binary contains /// the raw image and associated metadata (version, format, triple, etc.). /// \param Binary The image to containerize. /// \param ImageKind The format of the image, e.g. SPIR-V or CUBIN. -/// \param OffloadKind The expected consumer of the image, e.g. CUDA or OpenMP. +/// \param OffloadKind The expected consuming runtime of the image, e.g. CUDA or +/// OpenMP. /// \param ImageFlags Flags associated with the image, e.g. for AMDGPU the /// features. /// \param MetaData The key-value map of metadata to be associated with the @@ -174,7 +175,7 @@ LLVM_ABI Error containerizeImage(std::unique_ptr<MemoryBuffer> &Binary, MapVector<StringRef, StringRef> &MetaData); namespace intel { -/// Containerizes OpenMP SPIR-V image into inner OffloadBinary format. +/// Containerizes an OpenMP SPIR-V image into an OffloadBinary image. /// \param Binary The SPIR-V binary to containerize. /// \param CompileOpts Optional compilation options. /// \param LinkOpts Optional linking options. diff --git a/llvm/lib/Frontend/Offloading/Utility.cpp b/llvm/lib/Frontend/Offloading/Utility.cpp index 1ad0fe146c2f3..2cd7e2198f421 100644 --- a/llvm/lib/Frontend/Offloading/Utility.cpp +++ b/llvm/lib/Frontend/Offloading/Utility.cpp @@ -392,8 +392,8 @@ Error offloading::containerizeImage(std::unique_ptr<MemoryBuffer> &Img, InnerImage.TheOffloadKind = OffloadKind; InnerImage.Flags = ImageFlags; - for (const auto &KV : MetaData) - InnerImage.StringData[KV.first] = KV.second; + for (const auto &[Key, Value] : MetaData) + InnerImage.StringData[Key] = Value; InnerImage.Image = std::move(Img); >From 46e2a654afae87ffb4c141d435dec495c1750d74 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Wed, 11 Mar 2026 03:50:10 -0700 Subject: [PATCH 18/31] [offload][l0][nfc] remove duplicated entry --- offload/plugins-nextgen/level_zero/src/L0Plugin.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp b/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp index fd8f36c28186a..7dc0b919ca5a5 100644 --- a/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp +++ b/offload/plugins-nextgen/level_zero/src/L0Plugin.cpp @@ -271,11 +271,6 @@ Error LevelZeroPluginTy::asyncBarrierImpl(omp_interop_val_t *Interop) { return Plugin::success(); } -// We only need to check for formats other than ELF here -Expected<bool> LevelZeroPluginTy::isImageCompatible(StringRef Image) const { - return identify_magic(Image) == file_magic::spirv_object; -} - } // namespace llvm::omp::target::plugin extern "C" { >From 02260850eed10c3d972686f6807ba9ae93abc77f Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Wed, 11 Mar 2026 04:15:48 -0700 Subject: [PATCH 19/31] Pass triple as argument --- clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp | 2 +- llvm/include/llvm/Frontend/Offloading/Utility.h | 8 +++++--- llvm/lib/Frontend/Offloading/Utility.cpp | 9 +++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp index 54fad8a6ed5e7..9e24a9c26d897 100644 --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -652,7 +652,7 @@ Error containerizeRawImage(std::unique_ptr<MemoryBuffer> &Img, OffloadKind Kind, llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ)); if (Kind == OFK_OpenMP && Triple.isSPIRV() && Triple.getVendor() == llvm::Triple::Intel) - return offloading::intel::containerizeOpenMPSPIRVImage(Img); + return offloading::intel::containerizeOpenMPSPIRVImage(Img, Triple); return Error::success(); } diff --git a/llvm/include/llvm/Frontend/Offloading/Utility.h b/llvm/include/llvm/Frontend/Offloading/Utility.h index 656cdcf6b4d5d..db04d1fa25411 100644 --- a/llvm/include/llvm/Frontend/Offloading/Utility.h +++ b/llvm/include/llvm/Frontend/Offloading/Utility.h @@ -161,6 +161,7 @@ LLVM_ABI Error getAMDGPUMetaDataFromImage( /// Creates a nested OffloadBinary structure where the inner binary contains /// the raw image and associated metadata (version, format, triple, etc.). /// \param Binary The image to containerize. +/// \param Triple The target triple to be associated with the image. /// \param ImageKind The format of the image, e.g. SPIR-V or CUBIN. /// \param OffloadKind The expected consuming runtime of the image, e.g. CUDA or /// OpenMP. @@ -169,7 +170,7 @@ LLVM_ABI Error getAMDGPUMetaDataFromImage( /// \param MetaData The key-value map of metadata to be associated with the /// image. LLVM_ABI Error containerizeImage(std::unique_ptr<MemoryBuffer> &Binary, - object::ImageKind ImageKind, + llvm::Triple Triple, object::ImageKind ImageKind, object::OffloadKind OffloadKind, int32_t ImageFlags, MapVector<StringRef, StringRef> &MetaData); @@ -177,11 +178,12 @@ LLVM_ABI Error containerizeImage(std::unique_ptr<MemoryBuffer> &Binary, namespace intel { /// Containerizes an OpenMP SPIR-V image into an OffloadBinary image. /// \param Binary The SPIR-V binary to containerize. +/// \param Triple The target triple to be associated with the image. /// \param CompileOpts Optional compilation options. /// \param LinkOpts Optional linking options. LLVM_ABI Error containerizeOpenMPSPIRVImage( - std::unique_ptr<MemoryBuffer> &Binary, StringRef CompileOpts = "", - StringRef LinkOpts = ""); + std::unique_ptr<MemoryBuffer> &Binary, llvm::Triple Triple, + StringRef CompileOpts = "", StringRef LinkOpts = ""); } // namespace intel } // namespace offloading } // namespace llvm diff --git a/llvm/lib/Frontend/Offloading/Utility.cpp b/llvm/lib/Frontend/Offloading/Utility.cpp index 2cd7e2198f421..f46d0ec34fa48 100644 --- a/llvm/lib/Frontend/Offloading/Utility.cpp +++ b/llvm/lib/Frontend/Offloading/Utility.cpp @@ -380,6 +380,7 @@ Error llvm::offloading::amdgpu::getAMDGPUMetaDataFromImage( } Error offloading::containerizeImage(std::unique_ptr<MemoryBuffer> &Img, + llvm::Triple Triple, object::ImageKind ImageKind, object::OffloadKind OffloadKind, int32_t ImageFlags, @@ -392,6 +393,7 @@ Error offloading::containerizeImage(std::unique_ptr<MemoryBuffer> &Img, InnerImage.TheOffloadKind = OffloadKind; InnerImage.Flags = ImageFlags; + InnerImage.StringData["triple"] = Triple.getTriple(); for (const auto &[Key, Value] : MetaData) InnerImage.StringData[Key] = Value; @@ -404,17 +406,16 @@ Error offloading::containerizeImage(std::unique_ptr<MemoryBuffer> &Img, } Error offloading::intel::containerizeOpenMPSPIRVImage( - std::unique_ptr<MemoryBuffer> &Binary, StringRef CompileOpts, - StringRef LinkOpts) { + std::unique_ptr<MemoryBuffer> &Binary, llvm::Triple Triple, + StringRef CompileOpts, StringRef LinkOpts) { MapVector<StringRef, StringRef> MetaData; MetaData["version"] = "1.0"; - MetaData["triple"] = "spirv64-intel"; if (!CompileOpts.empty()) MetaData["compile-opts"] = CompileOpts; if (!LinkOpts.empty()) MetaData["link-opts"] = LinkOpts; - return containerizeImage(Binary, object::ImageKind::IMG_SPIRV, + return containerizeImage(Binary, Triple, object::ImageKind::IMG_SPIRV, object::OffloadKind::OFK_OpenMP, /*ImageFlags=*/0, MetaData); } >From c9b8d9339b33b24425e1785a4cbac9f548c9cdb8 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Wed, 11 Mar 2026 04:23:40 -0700 Subject: [PATCH 20/31] format --- llvm/include/llvm/Frontend/Offloading/Utility.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/llvm/include/llvm/Frontend/Offloading/Utility.h b/llvm/include/llvm/Frontend/Offloading/Utility.h index db04d1fa25411..eb08e7ec661e4 100644 --- a/llvm/include/llvm/Frontend/Offloading/Utility.h +++ b/llvm/include/llvm/Frontend/Offloading/Utility.h @@ -170,7 +170,8 @@ LLVM_ABI Error getAMDGPUMetaDataFromImage( /// \param MetaData The key-value map of metadata to be associated with the /// image. LLVM_ABI Error containerizeImage(std::unique_ptr<MemoryBuffer> &Binary, - llvm::Triple Triple, object::ImageKind ImageKind, + llvm::Triple Triple, + object::ImageKind ImageKind, object::OffloadKind OffloadKind, int32_t ImageFlags, MapVector<StringRef, StringRef> &MetaData); >From 727173a732b4e8e087debd2a775049b21a7664ae Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Wed, 11 Mar 2026 05:56:15 -0700 Subject: [PATCH 21/31] update tests --- llvm/test/tools/llvm-objdump/Offloading/coff.test | 4 ++++ llvm/test/tools/llvm-objdump/Offloading/elf.test | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/llvm/test/tools/llvm-objdump/Offloading/coff.test b/llvm/test/tools/llvm-objdump/Offloading/coff.test index 022277d137bd4..5bec7d60cb83e 100644 --- a/llvm/test/tools/llvm-objdump/Offloading/coff.test +++ b/llvm/test/tools/llvm-objdump/Offloading/coff.test @@ -22,21 +22,25 @@ symbols: # CHECK-NEXT:arch gfx908 # CHECK-NEXT:triple amdgcn-amd-amdhsa # CHECK-NEXT:producer openmp +# CHECK-NEXT:image size 0 bytes # CHECK-EMPTY: # CHECK-NEXT:OFFLOADING IMAGE [1]: # CHECK-NEXT:kind llvm ir # CHECK-NEXT:arch gfx90a # CHECK-NEXT:triple amdgcn-amd-amdhsa # CHECK-NEXT:producer openmp +# CHECK-NEXT:image size 0 bytes # CHECK-EMPTY: # CHECK-NEXT:OFFLOADING IMAGE [2]: # CHECK-NEXT:kind cubin # CHECK-NEXT:arch sm_52 # CHECK-NEXT:triple nvptx64-nvidia-cuda # CHECK-NEXT:producer openmp +# CHECK-NEXT:image size 0 bytes # CHECK-EMPTY: # CHECK-NEXT:OFFLOADING IMAGE [3]: # CHECK-NEXT:kind <none> # CHECK-NEXT:arch sm_70 # CHECK-NEXT:triple nvptx64-nvidia-cuda # CHECK-NEXT:producer none +# CHECK-NEXT:image size 0 bytes diff --git a/llvm/test/tools/llvm-objdump/Offloading/elf.test b/llvm/test/tools/llvm-objdump/Offloading/elf.test index 10182aeb856cd..3064286b9fea1 100644 --- a/llvm/test/tools/llvm-objdump/Offloading/elf.test +++ b/llvm/test/tools/llvm-objdump/Offloading/elf.test @@ -31,21 +31,25 @@ Sections: # CHECK-NEXT:arch gfx908 # CHECK-NEXT:triple amdgcn-amd-amdhsa # CHECK-NEXT:producer openmp +# CHECK-NEXT:image size 0 bytes # CHECK-EMPTY: # CHECK-NEXT:OFFLOADING IMAGE [1]: # CHECK-NEXT:kind llvm ir # CHECK-NEXT:arch gfx90a # CHECK-NEXT:triple amdgcn-amd-amdhsa # CHECK-NEXT:producer openmp +# CHECK-NEXT:image size 0 bytes # CHECK-EMPTY: # CHECK-NEXT:OFFLOADING IMAGE [2]: # CHECK-NEXT:kind cubin # CHECK-NEXT:arch sm_52 # CHECK-NEXT:triple nvptx64-nvidia-cuda # CHECK-NEXT:producer openmp +# CHECK-NEXT:image size 0 bytes # CHECK-EMPTY: # CHECK-NEXT:OFFLOADING IMAGE [3]: # CHECK-NEXT:kind <none> # CHECK-NEXT:arch sm_70 # CHECK-NEXT:triple nvptx64-nvidia-cuda # CHECK-NEXT:producer none +# CHECK-NEXT:image size 0 bytes >From 8158cef4ede45d855551867ab25141cd06b1c172 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Wed, 11 Mar 2026 15:00:29 -0700 Subject: [PATCH 22/31] support arbitrary levels of nesting --- llvm/tools/llvm-objdump/OffloadDump.cpp | 45 +++++++++++++++---------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/llvm/tools/llvm-objdump/OffloadDump.cpp b/llvm/tools/llvm-objdump/OffloadDump.cpp index ffe4d52d30446..9f9c200236bd1 100644 --- a/llvm/tools/llvm-objdump/OffloadDump.cpp +++ b/llvm/tools/llvm-objdump/OffloadDump.cpp @@ -44,24 +44,32 @@ static StringRef getImageName(const OffloadBinary &OB) { } } +std::string Indent(uint64_t Level) { + return std::string(Level * 2, ' '); +} + static void printOffloadBinaryMetadata(const OffloadBinary &OB, uint64_t level) { - const std::string Indent(level * 2, ' '); + const std::string IndentStr = Indent(level); - outs() << Indent << left_justify("kind", 16) << getImageName(OB) << "\n"; - outs() << Indent << left_justify("arch", 16) << OB.getArch() << "\n"; - outs() << Indent << left_justify("triple", 16) << OB.getTriple() << "\n"; - outs() << Indent << left_justify("producer", 16) + outs() << IndentStr << left_justify("kind", 16) << getImageName(OB) << "\n"; + outs() << IndentStr << left_justify("arch", 16) << OB.getArch() << "\n"; + outs() << IndentStr << left_justify("triple", 16) << OB.getTriple() << "\n"; + outs() << IndentStr << left_justify("producer", 16) << getOffloadKindName(OB.getOffloadKind()) << "\n"; StringRef InnerImage = OB.getImage(); - outs() << Indent << left_justify("image size", 16) << InnerImage.size() + outs() << IndentStr << left_justify("image size", 16) << InnerImage.size() << " bytes\n"; } +static void printBinary(const OffloadBinary &OB, uint64_t Index, + uint64_t Level = 0, std::string ParentIndex = ""); + /// Print information about nested OffloadBinary (inner layer) static void printNestedOffloadBinary(const OffloadBinary &OuterOB, - uint64_t Index) { + uint64_t Index, uint64_t Level, + std::string ParentIndex) { StringRef ImageData = OuterOB.getImage(); // Parse inner OffloadBinary @@ -81,28 +89,29 @@ static void printNestedOffloadBinary(const OffloadBinary &OuterOB, return; } - outs() << " [Nested OffloadBinary format detected]\n"; - outs() << " Number of inner images: " << InnerBinaries.size() << "\n"; + outs() << Indent(Level) << left_justify("nested images", 16) + << InnerBinaries.size() << "\n"; // Display information for each inner image for (uint64_t I = 0, E = InnerBinaries.size(); I != E; ++I) { const OffloadBinary *InnerOB = InnerBinaries[I].get(); - - if (InnerBinaries.size() > 1) - outs() << " Inner image [" << I << "]:\n"; - - printOffloadBinaryMetadata(*InnerOB, 1); + printBinary(*InnerOB, I, Level + 1, ParentIndex); } } -static void printBinary(const OffloadBinary &OB, uint64_t Index) { - outs() << "\nOFFLOADING IMAGE [" << Index << "]:\n"; - printOffloadBinaryMetadata(OB, 0); +static void printBinary(const OffloadBinary &OB, uint64_t Index, uint64_t Level, + std::string ParentIndex) { + outs() << "\n" + << Indent(Level) << "OFFLOADING IMAGE [" << ParentIndex << Index + << "]:\n"; + + printOffloadBinaryMetadata(OB, Level); StringRef ImageData = OB.getImage(); // Check for nested OffloadBinary format if (identify_magic(ImageData) == file_magic::offload_binary) - printNestedOffloadBinary(OB, Index); + printNestedOffloadBinary(OB, Index, Level, + ParentIndex + std::to_string(Index) + "."); } /// Print the embedded offloading contents of an ObjectFile \p O. >From d7b49b35b208d61c2a41c489dc9041dac3cdf803 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Wed, 11 Mar 2026 15:28:34 -0700 Subject: [PATCH 23/31] format --- llvm/tools/llvm-objdump/OffloadDump.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/llvm/tools/llvm-objdump/OffloadDump.cpp b/llvm/tools/llvm-objdump/OffloadDump.cpp index 9f9c200236bd1..3fc2466803c4c 100644 --- a/llvm/tools/llvm-objdump/OffloadDump.cpp +++ b/llvm/tools/llvm-objdump/OffloadDump.cpp @@ -44,9 +44,7 @@ static StringRef getImageName(const OffloadBinary &OB) { } } -std::string Indent(uint64_t Level) { - return std::string(Level * 2, ' '); -} +std::string Indent(uint64_t Level) { return std::string(Level * 2, ' '); } static void printOffloadBinaryMetadata(const OffloadBinary &OB, uint64_t level) { >From 705925b037877fd9a1678ade3ce4737850bc5d1b Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Wed, 11 Mar 2026 15:45:38 -0700 Subject: [PATCH 24/31] fix test --- clang/test/Tooling/clang-linker-wrapper-spirv.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clang/test/Tooling/clang-linker-wrapper-spirv.cpp b/clang/test/Tooling/clang-linker-wrapper-spirv.cpp index 74d1fb2c212c1..ecbfe626129db 100644 --- a/clang/test/Tooling/clang-linker-wrapper-spirv.cpp +++ b/clang/test/Tooling/clang-linker-wrapper-spirv.cpp @@ -5,9 +5,8 @@ // RUN: %clangxx -fopenmp -fopenmp-targets=spirv64-intel -nogpulib -o %t %s // RUN: llvm-objdump --offloading %t | FileCheck -check-prefix=CHECK %s -// CHECK: [Nested OffloadBinary -// CHECK: Number of inner images: 1 -// CHECK: spirv64-intel +// CHECK: nested images 1 +// CHECK: triple spirv64-intel int main(int argc, char** argv) { return 0; >From 99c5d2dc954263165219820feeb2d59f1879da3a Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Thu, 12 Mar 2026 22:33:09 -0700 Subject: [PATCH 25/31] keep current archive behavior --- .../llvm-offload-binary.cpp | 30 +------------------ 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp b/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp index 8a06b5e2ce38f..2444a13fdee46 100644 --- a/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp +++ b/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp @@ -137,29 +137,6 @@ static Error bundleImages() { return Error::success(); } -// Recursively unwrap nested OffloadBinaries to get the actual device image. -static Expected<StringRef> unwrapImage(StringRef ImageData) { - // Check if the image contains a nested OffloadBinary. - if (identify_magic(ImageData) == file_magic::offload_binary) { - // Parse nested OffloadBinary. - MemoryBufferRef InnerBuffer(ImageData, "nested-offload-binary"); - auto InnerBinaries = OffloadBinary::create(InnerBuffer); - if (!InnerBinaries) - return InnerBinaries.takeError(); - - // For single entry, recursively unwrap. - if (InnerBinaries->size() == 1) - return unwrapImage((*InnerBinaries)[0]->getImage()); - - // Multiple entries not supported for single file extraction. - return createStringError(inconvertibleErrorCode(), - "nested OffloadBinary contains multiple entries"); - } - - // Base case: return the actual device image. - return ImageData; -} - // Extract a single OffloadBinary, recursively handling nested OffloadBinaries. static Error extractBinary(const OffloadBinary *Binary, StringRef InputFile, uint64_t &Idx, StringSaver &Saver) { @@ -271,12 +248,7 @@ static Error unbundleImages() { WithColor::warning(errs(), PackagerExecutable) << "Multiple inputs match to a single file, '" << It->second << "'\n"; - const OffloadBinary *Binary = Extracted.back(); - // Recursively unwrap any nested OffloadBinaries. - auto ImageOrErr = unwrapImage(Binary->getImage()); - if (!ImageOrErr) - return ImageOrErr.takeError(); - if (Error E = writeFile(It->second, *ImageOrErr)) + if (Error E = writeFile(It->second, Extracted.back()->getImage())) return E; } else { uint64_t Idx = 0; >From 08d59344e8e23b91da19dc7054c7793de87722dc Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Fri, 13 Mar 2026 03:58:45 -0700 Subject: [PATCH 26/31] fix error in extraction; add test --- .../tools/llvm-offload-binary/llvm-offload-binary.ll | 8 +++++++- llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp | 10 +++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll b/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll index e410e033baa73..37202820e30a2 100644 --- a/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll +++ b/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll @@ -18,5 +18,11 @@ ; Test extracting all images without specifying --image filters. ; RUN: llvm-offload-binary %t | FileCheck --check-prefix=EXTRACT %s - ; EXTRACT: Extracted: llvm-offload-binary.{{.*}}-x-y-z-abc.0. + +; Test extracting nested images. +; RUN: llvm-offload-binary -o %t5 --image=file=%s,file=%s,arch=abc,triple=x-y-z +; RUN: llvm-offload-binary -o %t6 --image=file=%t5,arch=nested,triple=x-y-z +; RUN: llvm-offload-binary %t6 | FileCheck --check-prefix=EXTRACT-NESTED %s +; EXTRACT-NESTED: Extracted: llvm-offload-binary.{{.*}}-x-y-z-abc.0. +; EXTRACT-NESTED: Extracted: llvm-offload-binary.{{.*}}-x-y-z-abc.1. \ No newline at end of file diff --git a/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp b/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp index 2444a13fdee46..1babc32251db9 100644 --- a/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp +++ b/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp @@ -146,13 +146,13 @@ static Error extractBinary(const OffloadBinary *Binary, StringRef InputFile, if (identify_magic(ImageData) == file_magic::offload_binary) { // Parse nested OffloadBinary. MemoryBufferRef InnerBuffer(ImageData, "nested-offload-binary"); - auto InnerBinaries = OffloadBinary::create(InnerBuffer); - if (!InnerBinaries) - return InnerBinaries.takeError(); + SmallVector<OffloadFile> InnerBinaries; + if (Error Err = extractOffloadBinaries(InnerBuffer, InnerBinaries)) + return Err; // Recursively extract each nested binary. - for (const auto &InnerBinary : *InnerBinaries) { - if (Error E = extractBinary(InnerBinary.get(), InputFile, Idx, Saver)) + for (const auto &InnerBinary : InnerBinaries) { + if (Error E = extractBinary(InnerBinary.getBinary(), InputFile, Idx, Saver)) return E; } return Error::success(); >From 5af984488477ad6a66892dc23a60faae43b96ad8 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Fri, 13 Mar 2026 04:27:54 -0700 Subject: [PATCH 27/31] don't use OffloadBinary::create; add test --- .../llvm-offload-binary/llvm-offload-binary.ll | 11 +++++++++++ llvm/tools/llvm-objdump/OffloadDump.cpp | 13 ++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll b/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll index df46ad3a0d38a..e9fdd1ad1a481 100644 --- a/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll +++ b/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll @@ -15,3 +15,14 @@ ; RUN: llvm-offload-binary -o %t3 --image=file=%s ; RUN: llvm-offload-binary %t3 --image=file=%t4 ; RUN: diff %s %t4 + +; Test nested OffloadBinary construction with mulitiple inner images +; RUN: llvm-offload-binary -o %t5 --image=file=%s,arch=abc,triple=x-y-z --image=file=%s,arch=def,triple=x-y-z +; RUN: llvm-offload-binary -o %t6 --image=file=%t5,arch=nested,triple=x-y-z +; RUN: llvm-objdump --offloading %t6 | FileCheck %s --check-prefix=NESTED + +; NESTED: OFFLOADING IMAGE [0]: +; NESTED: arch nested +; NESTED: nested images 2 +; NESTED: OFFLOADING IMAGE [0.0]: +; NESTED: OFFLOADING IMAGE [0.1]: \ No newline at end of file diff --git a/llvm/tools/llvm-objdump/OffloadDump.cpp b/llvm/tools/llvm-objdump/OffloadDump.cpp index 3fc2466803c4c..31358a4004851 100644 --- a/llvm/tools/llvm-objdump/OffloadDump.cpp +++ b/llvm/tools/llvm-objdump/OffloadDump.cpp @@ -72,15 +72,14 @@ static void printNestedOffloadBinary(const OffloadBinary &OuterOB, // Parse inner OffloadBinary MemoryBufferRef InnerBuffer(ImageData, "inner-offload-binary"); - auto InnerBinariesOrErr = OffloadBinary::create(InnerBuffer); - if (!InnerBinariesOrErr) { - reportWarning("failed to parse nested OffloadBinary: " + - toString(InnerBinariesOrErr.takeError()), + llvm::SmallVector<OffloadFile> InnerBinaries; + auto Err = extractOffloadBinaries(InnerBuffer, InnerBinaries); + if (Err) { + reportWarning("failed to extract nested OffloadBinary: " + + toString(std::move(Err)), OuterOB.getFileName()); return; } - - auto &InnerBinaries = *InnerBinariesOrErr; if (InnerBinaries.empty()) { reportWarning("nested OffloadBinary contains no entries", OuterOB.getFileName()); @@ -92,7 +91,7 @@ static void printNestedOffloadBinary(const OffloadBinary &OuterOB, // Display information for each inner image for (uint64_t I = 0, E = InnerBinaries.size(); I != E; ++I) { - const OffloadBinary *InnerOB = InnerBinaries[I].get(); + const OffloadBinary *InnerOB = InnerBinaries[I].getBinary(); printBinary(*InnerOB, I, Level + 1, ParentIndex); } } >From f79b7b3b1658425bbc8c8170c19a83943ce42c98 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Fri, 13 Mar 2026 04:38:51 -0700 Subject: [PATCH 28/31] Add more tests --- .../llvm-offload-binary.ll | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll b/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll index 6a48e29e89d09..dde6894951a54 100644 --- a/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll +++ b/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll @@ -18,9 +18,10 @@ ; Test extracting all images without specifying --image filters. ; RUN: llvm-offload-binary %t | FileCheck --check-prefix=EXTRACT %s + ; EXTRACT: Extracted: llvm-offload-binary.{{.*}}-x-y-z-abc.0. -; Test nested OffloadBinary construction with mulitiple inner images +; Test nested OffloadBinary construction with multiple inner images. ; RUN: llvm-offload-binary -o %t5 --image=file=%s,arch=abc,triple=x-y-z --image=file=%s,arch=def,triple=x-y-z ; RUN: llvm-offload-binary -o %t6 --image=file=%t5,arch=nested,triple=x-y-z ; RUN: llvm-objdump --offloading %t6 | FileCheck %s --check-prefix=NESTED @@ -32,8 +33,19 @@ ; NESTED: OFFLOADING IMAGE [0.1]: ; Test extracting nested images. -; RUN: llvm-offload-binary -o %t5 --image=file=%s,file=%s,arch=abc,triple=x-y-z -; RUN: llvm-offload-binary -o %t6 --image=file=%t5,arch=nested,triple=x-y-z ; RUN: llvm-offload-binary %t6 | FileCheck --check-prefix=EXTRACT-NESTED %s + ; EXTRACT-NESTED: Extracted: llvm-offload-binary.{{.*}}-x-y-z-abc.0. -; EXTRACT-NESTED: Extracted: llvm-offload-binary.{{.*}}-x-y-z-abc.1. +; EXTRACT-NESTED: Extracted: llvm-offload-binary.{{.*}}-x-y-z-def.1. + +; Test mixed nested and non-nested images. +; RUN: llvm-offload-binary -o %t7 --image=file=%t5,arch=nested,triple=x-y-z --image=file=%s,arch=ghi,triple=x-y-z +; RUN: llvm-offload-binary %t7 | FileCheck --check-prefix=EXTRACT-MIXED %s + +; EXTRACT-MIXED: Extracted: llvm-offload-binary.{{.*}}-x-y-z-abc.0. +; EXTRACT-MIXED: Extracted: llvm-offload-binary.{{.*}}-x-y-z-def.1. +; EXTRACT-MIXED: Extracted: llvm-offload-binary.{{.*}}-x-y-z-ghi.2. + +; Test extracting inner OffloadBinary with --image filter. +; RUN: llvm-offload-binary %t7 --image=file=%t8,arch=nested,triple=x-y-z +; RUN: diff %t5 %t8 >From 27b9033d8e4a334a227f0acb341e3fed53d60ae2 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Fri, 13 Mar 2026 04:40:09 -0700 Subject: [PATCH 29/31] typo --- llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll b/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll index e9fdd1ad1a481..e2d970c59c0b2 100644 --- a/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll +++ b/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll @@ -16,7 +16,7 @@ ; RUN: llvm-offload-binary %t3 --image=file=%t4 ; RUN: diff %s %t4 -; Test nested OffloadBinary construction with mulitiple inner images +; Test nested OffloadBinary construction with multiple inner images. ; RUN: llvm-offload-binary -o %t5 --image=file=%s,arch=abc,triple=x-y-z --image=file=%s,arch=def,triple=x-y-z ; RUN: llvm-offload-binary -o %t6 --image=file=%t5,arch=nested,triple=x-y-z ; RUN: llvm-objdump --offloading %t6 | FileCheck %s --check-prefix=NESTED >From 9556ba38783f8b9b67005ef5eebf548df9cd5b51 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Fri, 13 Mar 2026 04:51:17 -0700 Subject: [PATCH 30/31] add multilevel test --- .../llvm-offload-binary.ll | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll b/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll index e2d970c59c0b2..6144c9692d83c 100644 --- a/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll +++ b/llvm/test/tools/llvm-offload-binary/llvm-offload-binary.ll @@ -25,4 +25,27 @@ ; NESTED: arch nested ; NESTED: nested images 2 ; NESTED: OFFLOADING IMAGE [0.0]: -; NESTED: OFFLOADING IMAGE [0.1]: \ No newline at end of file +; NESTED: OFFLOADING IMAGE [0.1]: + +; Test complex nested OffloadBinary construction with multiple levels. +; RUN: llvm-offload-binary -o %t7 --image=file=%s,arch=abc,triple=x-y-z --image=file=%t5,arch=nested,triple=x-y-z +; RUN: llvm-offload-binary -o %t8 --image=file=%t7,arch=nested,triple=x-y-z --image=file=%t5,arch=nested2,triple=x-y-z +; RUN: llvm-objdump --offloading %t8 | FileCheck %s --check-prefix=NESTED2 + +; NESTED2: OFFLOADING IMAGE [0]: +; NESTED2: arch nested +; NESTED2: nested images 2 +; NESTED2: OFFLOADING IMAGE [0.0]: +; NESTED2: arch abc +; NESTED2: OFFLOADING IMAGE [0.1]: +; NESTED2: arch nested +; NESTED2: nested images 2 +; NESTED2: OFFLOADING IMAGE [0.1.0]: +; NESTED2: OFFLOADING IMAGE [0.1.1]: +; NESTED2: OFFLOADING IMAGE [1]: +; NESTED2: arch nested2 +; NESTED2: nested images 2 +; NESTED2: OFFLOADING IMAGE [1.0]: +; NESTED2: arch abc +; NESTED2: OFFLOADING IMAGE [1.1]: +; NESTED2: arch def >From 3078c4704b19aa5131a0baab84dd805b30dcf212 Mon Sep 17 00:00:00 2001 From: "Duran, Alex" <[email protected]> Date: Fri, 13 Mar 2026 04:54:23 -0700 Subject: [PATCH 31/31] format --- llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp b/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp index 1babc32251db9..9270ee057619c 100644 --- a/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp +++ b/llvm/tools/llvm-offload-binary/llvm-offload-binary.cpp @@ -152,7 +152,8 @@ static Error extractBinary(const OffloadBinary *Binary, StringRef InputFile, // Recursively extract each nested binary. for (const auto &InnerBinary : InnerBinaries) { - if (Error E = extractBinary(InnerBinary.getBinary(), InputFile, Idx, Saver)) + if (Error E = + extractBinary(InnerBinary.getBinary(), InputFile, Idx, Saver)) return E; } return Error::success(); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
