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

Reply via email to