vzakhari created this revision.
vzakhari added reviewers: saiislam, jdoerfert.
Herald added a reviewer: alexander-shaposhnikov.
vzakhari requested review of this revision.
Herald added subscribers: cfe-commits, sstefan1.
Herald added a project: clang.

This code cannot currently work within llorg, because it is mostly based on 
https://github.com/intel/llvm state of clang-offload-wrapper.  This patch is 
uploaded to facilitate discussion at OpenMP at LLVM weekly meeting.

The code shows how to containerize several offload images passed to 
clang-offload-wrapper into a single ELF file.  It also re-implements the 
LLVMOMPOFFLOAD notes embedding that currently uses llvm-objcopy in llorg.

The implementation uses yaml::yaml2elf utility.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D112103

Files:
  clang/tools/clang-offload-wrapper/ClangOffloadWrapper.cpp

Index: clang/tools/clang-offload-wrapper/ClangOffloadWrapper.cpp
===================================================================
--- clang/tools/clang-offload-wrapper/ClangOffloadWrapper.cpp
+++ clang/tools/clang-offload-wrapper/ClangOffloadWrapper.cpp
@@ -26,6 +26,8 @@
 #include "llvm/IR/Module.h"
 #include "llvm/Object/ELFObjectFile.h"
 #include "llvm/Object/ObjectFile.h"
+#include "llvm/ObjectYAML/ELFYAML.h"
+#include "llvm/ObjectYAML/yaml2obj.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/EndianStream.h"
 #include "llvm/Support/Errc.h"
@@ -388,6 +390,181 @@
     return M;
   }
 
+  Expected<StringRef> getTmpFile(Twine NamePattern) {
+    Expected<sys::fs::TempFile> TempFile =
+        sys::fs::TempFile::create(NamePattern);
+    if (!TempFile)
+      return TempFile.takeError();
+
+    std::string FileName = TempFile->TmpName;
+
+    if (Error E = TempFile->keep(FileName))
+      return std::move(E);
+
+    TempFiles.push_back(std::move(FileName));
+    return TempFiles.back();
+  }
+
+  void containerizeOpenMPImages() {
+    // If there are no OpenMP images, there is nothing to do.
+    auto OpenMPPackIt = Packs.find(OffloadKind::OpenMP);
+    if (OpenMPPackIt == Packs.end())
+      return;
+    SameKindPack *OpenMPPack = OpenMPPackIt->second.get();
+
+    // Start creating notes for the ELF container.
+    std::vector<ELFYAML::NoteEntry> Notes;
+    std::string ImgVersion = toHex(OPENMP_OFFLOAD_IMAGE_VERSION);
+    Notes.emplace_back(ELFYAML::NoteEntry{"LLVMOMPOFFLOAD",
+        yaml::BinaryRef(ImgVersion),
+        ELF::NT_LLVM_OPENMP_OFFLOAD_VERSION});
+    std::string Producer = toHex("LLVM");
+    Notes.emplace_back(ELFYAML::NoteEntry{"LLVMOMPOFFLOAD",
+        yaml::BinaryRef(Producer),
+        ELF::NT_LLVM_OPENMP_OFFLOAD_PRODUCER});
+    std::string ProdVersion = toHex(LLVM_VERSION_STRING
+#ifdef LLVM_REVISION
+                                    " " LLVM_REVISION
+#endif
+                                    );
+    Notes.emplace_back(ELFYAML::NoteEntry{"LLVMOMPOFFLOAD",
+        yaml::BinaryRef(ProdVersion),
+        ELF::NT_LLVM_OPENMP_OFFLOAD_PRODUCER_VERSION});
+
+    SmallVector<StringRef, 2> ImageFiles;
+
+    // Find SPIR-V images and create notes with auxiliary information
+    // for each of them.
+    unsigned ImageIdx = 0;
+    for (auto I = OpenMPPack->begin(); I != OpenMPPack->end(); ++I) {
+      const BinaryWrapper::Image &Img = *(I->get());
+      if (Img.Tgt.find("spir") != 0)
+        continue;
+
+      ImageFiles.push_back(Img.File);
+      ++ImageIdx;
+    }
+
+    // If there are no SPIR-V images, there is nothing to do.
+    if (ImageIdx == 0)
+      return;
+
+    StringRef ToolNameRef(ToolName);
+
+    // Helper to emit warnings.
+    auto warningOS = [ToolNameRef]() -> raw_ostream & {
+      return WithColor::warning(errs(), ToolNameRef);
+    };
+    auto handleErrorAsWarning = [&warningOS](Error E) {
+      logAllUnhandledErrors(std::move(E), warningOS());
+    };
+
+    // Reserve  a temporary file for the ELF image.
+    Expected<StringRef> ImageFileName =
+        getTmpFile(Output + Twine(".spirv.elfimage.%%%%%%%.tmp"));
+    if (!ImageFileName) {
+      logAllUnhandledErrors(ImageFileName.takeError(), warningOS());
+      return;
+    }
+
+    std::error_code EC;
+    raw_fd_ostream ELFOS(ImageFileName->str(), EC);
+    if (EC) {
+      warningOS() << "cannot open ELF file " << ImageFileName->str() << ": "
+                  << EC.message() << "\n";
+      return;
+    }
+
+    // Write ELF file using yaml2elf instead of writing ELF by ourselves.
+    // We use 64-bit little-endian ELF currently.
+    ELFYAML::FileHeader Header{};
+    Header.Class = ELF::ELFCLASS64;
+    Header.Data = ELF::ELFDATA2LSB;
+    Header.Type = ELF::ET_NONE;
+    // Do not use any existing machine, otherwise, other plugins
+    // may claim this image.
+    Header.Machine = ELF::EM_NONE;
+
+    // Create a section with notes.
+    ELFYAML::NoteSection Section{};
+    Section.Type = ELF::SHT_NOTE;
+    Section.Name = ".note.openmp";
+    Section.Notes.emplace(std::move(Notes));
+
+    // The main ELFYAML structure describing our ELF container.
+    ELFYAML::Object Object{};
+    Object.Header = Header;
+    Object.Chunks.push_back(
+        std::make_unique<ELFYAML::NoteSection>(std::move(Section)));
+
+    SmallVector<std::unique_ptr<MemoryBuffer>, 4> Buffers;
+    SmallVector<std::string, 4> SectionNames;
+
+    ImageIdx = 0;
+    // Put each image into its own __openmp_offload_spirv_# section.
+    for (auto Name : ImageFiles) {
+      // Read the image into a memory buffer.
+      ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+          MemoryBuffer::getFile(Name);
+      if (!BufOrErr) {
+        handleErrorAsWarning(
+            createFileError(Name, BufOrErr.getError()));
+        return;
+      }
+      Buffers.push_back(std::move(*BufOrErr));
+
+      // Create a new section for the image.
+      ELFYAML::RawContentSection Section{};
+      Section.Info = yaml::Hex64(0);
+      Section.Type = ELF::SHT_PROGBITS;
+      SectionNames.emplace_back(
+          ("__openmp_offload_spirv_" + Twine(ImageIdx)).str());
+      Section.Name = SectionNames.back();
+      Section.Content = yaml::BinaryRef(makeArrayRef(
+          reinterpret_cast<const uint8_t *>(Buffers.back()->getBufferStart()),
+          Buffers.back()->getBufferSize()));
+
+      // Put the new section into the ELFYAML::Object.
+      Object.Chunks.push_back(
+          std::make_unique<ELFYAML::RawContentSection>(std::move(Section)));
+      ++ImageIdx;
+    }
+
+    yaml::ErrorHandler ErrHandler =
+        [&warningOS](const Twine &Msg) {
+          warningOS() << "cannot convert YAML to ELF: " << Msg << "\n";
+        };
+
+    if (!yaml::yaml2elf(Object, ELFOS, ErrHandler, UINT64_MAX)) {
+      warningOS() << "YAML to ELF conversion failed.\n";
+      return;
+    }
+    ELFOS.close();
+
+    if (ELFOS.has_error()) {
+      warningOS() << "cannot write ELF file " << ImageFileName->str() << ": "
+                  << ELFOS.error().message() << "\n";
+      ELFOS.clear_error();
+      return;
+    }
+
+    // Delete the original Images from the list and replace them
+    // with a single combined container ELF.
+    for (auto I = OpenMPPack->begin(); I != OpenMPPack->end();) {
+      const BinaryWrapper::Image &Img = *(I->get());
+      if (Img.Tgt.find("spir") != 0)
+        ++I;
+      else
+        I = OpenMPPack->erase(I);
+    }
+
+    OpenMPPack->emplace_back(std::make_unique<Image>(
+        *ImageFileName, "",
+        "spirv",
+        BinaryImageFormat::none, "", "",
+        "", ""));
+  }
+
   std::unique_ptr<MemoryBuffer> addELFNotes(std::unique_ptr<MemoryBuffer> Buf,
                                             StringRef OriginalFileName) {
     // Cannot add notes, if llvm-objcopy is not available.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to