[ObjectYAML] Add offloading binary implementations for obj2yaml and yaml2obj

This patchs adds the necessary code for inspecting or creating offloading
binaries using the standing `obj2yaml` and `yaml2obj` features in LLVM.

Depends on D127774

Reviewed By: jhenderson

Differential Revision: https://reviews.llvm.org/D127776

GitOrigin-RevId: 228c8f9cc0b2bf615706567f121a422b1b0e7f83
diff --git a/lib/ObjectYAML/CMakeLists.txt b/lib/ObjectYAML/CMakeLists.txt
index 818a174..15f8d85 100644
--- a/lib/ObjectYAML/CMakeLists.txt
+++ b/lib/ObjectYAML/CMakeLists.txt
@@ -18,6 +18,8 @@
   ObjectYAML.cpp
   MinidumpEmitter.cpp
   MinidumpYAML.cpp
+  OffloadEmitter.cpp
+  OffloadYAML.cpp
   WasmEmitter.cpp
   WasmYAML.cpp
   XCOFFEmitter.cpp
diff --git a/lib/ObjectYAML/ObjectYAML.cpp b/lib/ObjectYAML/ObjectYAML.cpp
index 8d72205..d57e558 100644
--- a/lib/ObjectYAML/ObjectYAML.cpp
+++ b/lib/ObjectYAML/ObjectYAML.cpp
@@ -56,6 +56,9 @@
     } else if (IO.mapTag("!minidump")) {
       ObjectFile.Minidump.reset(new MinidumpYAML::Object());
       MappingTraits<MinidumpYAML::Object>::mapping(IO, *ObjectFile.Minidump);
+    } else if (IO.mapTag("!Offload")) {
+      ObjectFile.Offload.reset(new OffloadYAML::Binary());
+      MappingTraits<OffloadYAML::Binary>::mapping(IO, *ObjectFile.Offload);
     } else if (IO.mapTag("!WASM")) {
       ObjectFile.Wasm.reset(new WasmYAML::Object());
       MappingTraits<WasmYAML::Object>::mapping(IO, *ObjectFile.Wasm);
diff --git a/lib/ObjectYAML/OffloadEmitter.cpp b/lib/ObjectYAML/OffloadEmitter.cpp
new file mode 100644
index 0000000..3ffbc4f
--- /dev/null
+++ b/lib/ObjectYAML/OffloadEmitter.cpp
@@ -0,0 +1,68 @@
+//===- OffloadEmitter.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Object/OffloadBinary.h"
+#include "llvm/ObjectYAML/OffloadYAML.h"
+#include "llvm/ObjectYAML/yaml2obj.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace OffloadYAML;
+
+namespace llvm {
+namespace yaml {
+
+bool yaml2offload(Binary &Doc, raw_ostream &Out, ErrorHandler EH) {
+  for (const auto &Member : Doc.Members) {
+    object::OffloadBinary::OffloadingImage Image{};
+    if (Member.ImageKind)
+      Image.TheImageKind = *Member.ImageKind;
+    if (Member.OffloadKind)
+      Image.TheOffloadKind = *Member.OffloadKind;
+    if (Member.Flags)
+      Image.Flags = *Member.Flags;
+
+    StringMap<StringRef> &StringData = Image.StringData;
+    if (Member.StringEntries) {
+      for (const auto &Entry : *Member.StringEntries) {
+        StringData[Entry.Key] = Entry.Value;
+      }
+    }
+
+    SmallVector<char, 1024> Data;
+    raw_svector_ostream OS(Data);
+    if (Member.Content)
+      Member.Content->writeAsBinary(OS);
+    Image.Image = MemoryBuffer::getMemBufferCopy(OS.str());
+
+    std::unique_ptr<MemoryBuffer> Binary = object::OffloadBinary::write(Image);
+
+    // Copy the data to a new buffer so we can modify the bytes directly.
+    SmallVector<char> NewBuffer;
+    std::copy(Binary->getBufferStart(), Binary->getBufferEnd(),
+              std::back_inserter(NewBuffer));
+    auto *TheHeader =
+        reinterpret_cast<object::OffloadBinary::Header *>(&NewBuffer[0]);
+    if (Doc.Version)
+      TheHeader->Version = *Doc.Version;
+    if (Doc.Size)
+      TheHeader->Size = *Doc.Size;
+    if (Doc.EntryOffset)
+      TheHeader->EntryOffset = *Doc.EntryOffset;
+    if (Doc.EntrySize)
+      TheHeader->EntrySize = *Doc.EntrySize;
+
+    Out.write(NewBuffer.begin(), NewBuffer.size());
+  }
+
+  return true;
+}
+
+} // namespace yaml
+} // namespace llvm
diff --git a/lib/ObjectYAML/OffloadYAML.cpp b/lib/ObjectYAML/OffloadYAML.cpp
new file mode 100644
index 0000000..d5a0edd
--- /dev/null
+++ b/lib/ObjectYAML/OffloadYAML.cpp
@@ -0,0 +1,78 @@
+//===- OffloadYAML.cpp - Offload Binary YAMLIO implementation -------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines classes for handling the YAML representation of offload
+// binaries.
+//
+//===----------------------------------------------------------------------===//
+
+#include <llvm/ObjectYAML/OffloadYAML.h>
+
+namespace llvm {
+
+namespace yaml {
+
+void ScalarEnumerationTraits<object::ImageKind>::enumeration(
+    IO &IO, object::ImageKind &Value) {
+#define ECase(X) IO.enumCase(Value, #X, object::X)
+  ECase(IMG_None);
+  ECase(IMG_Object);
+  ECase(IMG_Bitcode);
+  ECase(IMG_Cubin);
+  ECase(IMG_Fatbinary);
+  ECase(IMG_PTX);
+  ECase(IMG_LAST);
+#undef ECase
+  IO.enumFallback<Hex16>(Value);
+}
+
+void ScalarEnumerationTraits<object::OffloadKind>::enumeration(
+    IO &IO, object::OffloadKind &Value) {
+#define ECase(X) IO.enumCase(Value, #X, object::X)
+  ECase(OFK_None);
+  ECase(OFK_OpenMP);
+  ECase(OFK_Cuda);
+  ECase(OFK_HIP);
+  ECase(OFK_LAST);
+#undef ECase
+  IO.enumFallback<Hex16>(Value);
+}
+
+void MappingTraits<OffloadYAML::Binary>::mapping(IO &IO,
+                                                 OffloadYAML::Binary &O) {
+  assert(!IO.getContext() && "The IO context is initialized already");
+  IO.setContext(&O);
+  IO.mapTag("!Offload", true);
+  IO.mapOptional("Version", O.Version);
+  IO.mapOptional("Size", O.Size);
+  IO.mapOptional("EntryOffset", O.EntryOffset);
+  IO.mapOptional("EntrySize", O.EntrySize);
+  IO.mapRequired("Members", O.Members);
+  IO.setContext(nullptr);
+}
+
+void MappingTraits<OffloadYAML::Binary::StringEntry>::mapping(
+    IO &IO, OffloadYAML::Binary::StringEntry &SE) {
+  assert(IO.getContext() && "The IO context is not initialized");
+  IO.mapRequired("Key", SE.Key);
+  IO.mapRequired("Value", SE.Value);
+}
+
+void MappingTraits<OffloadYAML::Binary::Member>::mapping(
+    IO &IO, OffloadYAML::Binary::Member &M) {
+  assert(IO.getContext() && "The IO context is not initialized");
+  IO.mapOptional("ImageKind", M.ImageKind);
+  IO.mapOptional("OffloadKind", M.OffloadKind);
+  IO.mapOptional("Flags", M.Flags);
+  IO.mapOptional("String", M.StringEntries);
+  IO.mapOptional("Content", M.Content);
+}
+
+} // namespace yaml
+
+} // namespace llvm
diff --git a/lib/ObjectYAML/yaml2obj.cpp b/lib/ObjectYAML/yaml2obj.cpp
index 64c2599..06050e2 100644
--- a/lib/ObjectYAML/yaml2obj.cpp
+++ b/lib/ObjectYAML/yaml2obj.cpp
@@ -42,6 +42,8 @@
       return yaml2macho(Doc, Out, ErrHandler);
     if (Doc.Minidump)
       return yaml2minidump(*Doc.Minidump, Out, ErrHandler);
+    if (Doc.Offload)
+      return yaml2offload(*Doc.Offload, Out, ErrHandler);
     if (Doc.Wasm)
       return yaml2wasm(*Doc.Wasm, Out, ErrHandler);
     if (Doc.Xcoff)