jhuber6 updated this revision to Diff 417685.
jhuber6 added a comment.

Updating to use path instead of generic cmdline, makes it a lot easier to pass 
it. Also just adding a reserved field in case I want to add the cmdline back.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D122069/new/

https://reviews.llvm.org/D122069

Files:
  clang/include/clang/Basic/CodeGenOptions.h
  clang/include/clang/Basic/Offloading.h
  clang/lib/Basic/CMakeLists.txt
  clang/lib/Basic/Offloading.cpp
  clang/lib/CodeGen/BackendUtil.cpp
  clang/lib/Driver/ToolChains/Clang.cpp
  clang/test/Driver/openmp-offload-gpu.c
  clang/test/Frontend/embed-object.c
  clang/test/Frontend/embed-object.ll
  clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
  llvm/include/llvm/Transforms/Utils/ModuleUtils.h
  llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
  llvm/lib/Transforms/Utils/ModuleUtils.cpp

Index: llvm/lib/Transforms/Utils/ModuleUtils.cpp
===================================================================
--- llvm/lib/Transforms/Utils/ModuleUtils.cpp
+++ llvm/lib/Transforms/Utils/ModuleUtils.cpp
@@ -265,15 +265,15 @@
 }
 
 void llvm::embedBufferInModule(Module &M, MemoryBufferRef Buf,
-                               StringRef SectionName) {
-  // Embed the buffer into the module.
+                               StringRef SectionName, Align Alignment) {
+  // Embed the memory buffer into the module.
   Constant *ModuleConstant = ConstantDataArray::get(
       M.getContext(), makeArrayRef(Buf.getBufferStart(), Buf.getBufferSize()));
   GlobalVariable *GV = new GlobalVariable(
-      M, ModuleConstant->getType(), true, GlobalValue::ExternalLinkage,
-      ModuleConstant, SectionName.drop_front());
+      M, ModuleConstant->getType(), true, GlobalValue::PrivateLinkage,
+      ModuleConstant, "llvm.embedded.object");
   GV->setSection(SectionName);
-  GV->setVisibility(GlobalValue::HiddenVisibility);
+  GV->setAlignment(Alignment);
 
   appendToCompilerUsed(M, GV);
 }
Index: llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
===================================================================
--- llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
+++ llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
@@ -447,7 +447,7 @@
       Name == getInstrProfSectionName(IPSK_covfun, Triple::ELF,
                                       /*AddSegmentInfo=*/false) ||
       Name == ".llvmbc" || Name == ".llvmcmd" ||
-      Name.startswith(".llvm.offloading."))
+      Name.startswith(".llvm.offloading"))
     return SectionKind::getMetadata();
 
   if (Name.empty() || Name[0] != '.') return K;
Index: llvm/include/llvm/Transforms/Utils/ModuleUtils.h
===================================================================
--- llvm/include/llvm/Transforms/Utils/ModuleUtils.h
+++ llvm/include/llvm/Transforms/Utils/ModuleUtils.h
@@ -14,6 +14,7 @@
 #define LLVM_TRANSFORMS_UTILS_MODULEUTILS_H
 
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Alignment.h"
 #include "llvm/Support/MemoryBufferRef.h"
 #include <utility> // for std::pair
 
@@ -109,7 +110,8 @@
 
 /// Embed the memory buffer \p Buf into the module \p M as a global using the
 /// specified section name.
-void embedBufferInModule(Module &M, MemoryBufferRef Buf, StringRef SectionName);
+void embedBufferInModule(Module &M, MemoryBufferRef Buf, StringRef SectionName,
+                         Align Alignment = Align(1));
 
 class CallInst;
 namespace VFABI {
Index: clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
===================================================================
--- clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
+++ clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
@@ -15,6 +15,7 @@
 //===---------------------------------------------------------------------===//
 
 #include "OffloadWrapper.h"
+#include "clang/Basic/Offloading.h"
 #include "clang/Basic/Version.h"
 #include "llvm/BinaryFormat/Magic.h"
 #include "llvm/Bitcode/BitcodeWriter.h"
@@ -47,6 +48,7 @@
 #include "llvm/Target/TargetMachine.h"
 
 using namespace llvm;
+using namespace clang;
 using namespace llvm::object;
 
 static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
@@ -146,9 +148,10 @@
 static codegen::RegisterCodeGenFlags CodeGenFlags;
 
 /// Magic section string that marks the existence of offloading data. The
-/// section string will be formatted as `.llvm.offloading.<triple>.<arch>`.
-#define OFFLOAD_SECTION_MAGIC_STR ".llvm.offloading."
+/// section will contain one or more offloading binaries stored contiguously.
+#define OFFLOAD_SECTION_MAGIC_STR ".llvm.offloading"
 
+// TODO: Replace all uses of DeviceFile with OffloadFile.
 /// Information for a device offloading file extracted from the host.
 struct DeviceFile {
   DeviceFile(StringRef Kind, StringRef TheTriple, StringRef Arch,
@@ -201,16 +204,6 @@
     llvm::errs() << *IC << (std::next(IC) != IE ? " " : "\n");
 }
 
-static StringRef getDeviceFileExtension(StringRef DeviceTriple,
-                                        bool IsBitcode = false) {
-  Triple TheTriple(DeviceTriple);
-  if (TheTriple.isAMDGPU() || IsBitcode)
-    return "bc";
-  if (TheTriple.isNVPTX())
-    return "cubin";
-  return "o";
-}
-
 std::string getMainExecutable(const char *Name) {
   void *Ptr = (void *)(intptr_t)&getMainExecutable;
   auto COWPath = sys::fs::getMainExecutable(Name, Ptr);
@@ -296,39 +289,50 @@
   StringRef Prefix = sys::path::stem(Obj.getFileName());
   SmallVector<StringRef, 4> ToBeStripped;
 
-  // Extract data from sections of the form `.llvm.offloading.<triple>.<arch>`.
+  // Extract offloading binaries from sections with the name `.llvm.offloading`.
   for (const SectionRef &Sec : Obj.sections()) {
     Expected<StringRef> Name = Sec.getName();
-    if (!Name || !Name->startswith(OFFLOAD_SECTION_MAGIC_STR))
+    if (!Name || !Name->equals(OFFLOAD_SECTION_MAGIC_STR))
       continue;
 
-    SmallVector<StringRef, 4> SectionFields;
-    Name->split(SectionFields, '.');
-    StringRef Kind = SectionFields[3];
-    StringRef DeviceTriple = SectionFields[4];
-    StringRef Arch = SectionFields[5];
+    Expected<StringRef> Contents = Sec.getContents();
+    if (!Contents)
+      return Contents.takeError();
+
+    uint64_t Offset = 0;
+    // There could be multiple offloading binaries stored at this section.
+    while (Offset < Contents->size()) {
+      std::unique_ptr<MemoryBuffer> Buffer =
+          MemoryBuffer::getMemBuffer(Contents->drop_front(Offset), *Name,
+                                     /*RequiresNullTerminator*/ false);
+      auto BinaryOrErr = OffloadBinary::create(*Buffer);
+      if (!BinaryOrErr)
+        return BinaryOrErr.takeError();
+      OffloadBinary &Binary = **BinaryOrErr;
+
+      StringRef Kind =
+          getOffloadKindName(static_cast<OffloadKind>(Binary.getOffloadKind()));
+      StringRef Suffix =
+          getImageKindName(static_cast<ImageKind>(Binary.getImageKind()));
 
-    if (Expected<StringRef> Contents = Sec.getContents()) {
       SmallString<128> TempFile;
-      StringRef DeviceExtension = getDeviceFileExtension(
-          DeviceTriple, identify_magic(*Contents) == file_magic::bitcode);
       if (Error Err = createOutputFile(Prefix + "-" + Kind + "-" +
-                                           DeviceTriple + "-" + Arch,
-                                       DeviceExtension, TempFile))
+                                           Binary.getTripleStr() + "-" +
+                                           Binary.getArch(),
+                                       Suffix, TempFile))
         return std::move(Err);
 
-      Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr =
-          FileOutputBuffer::create(TempFile, Sec.getSize());
-      if (!OutputOrErr)
-        return OutputOrErr.takeError();
-      std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr);
-      std::copy(Contents->begin(), Contents->end(), Output->getBufferStart());
-      if (Error E = Output->commit())
-        return std::move(E);
-
-      DeviceFiles.emplace_back(Kind, DeviceTriple, Arch, TempFile);
-      ToBeStripped.push_back(*Name);
+      Expected<OffloadFile> File = Binary.createOffloadFile(TempFile);
+      if (!File)
+        return File.takeError();
+
+      DeviceFiles.emplace_back(Kind, File->TheTriple, File->Arch,
+                               File->Filename);
+
+      Offset += Binary.getSize();
     }
+
+    ToBeStripped.push_back(*Name);
   }
 
   if (ToBeStripped.empty() || !StripSections)
@@ -405,42 +409,50 @@
 
   SmallVector<GlobalVariable *, 4> ToBeDeleted;
 
-  // Extract data from the global string containing a section of the form
-  // `.llvm.offloading.<triple>.<arch>`.
+  // Extract offloading data from globals with the `.llvm.offloading` section
+  // name.
   for (GlobalVariable &GV : M->globals()) {
-    if (!GV.hasSection() ||
-        !GV.getSection().startswith(OFFLOAD_SECTION_MAGIC_STR))
+    if (!GV.hasSection() || !GV.getSection().equals(OFFLOAD_SECTION_MAGIC_STR))
       continue;
 
     auto *CDS = dyn_cast<ConstantDataSequential>(GV.getInitializer());
     if (!CDS)
       continue;
 
-    SmallVector<StringRef, 4> SectionFields;
-    GV.getSection().split(SectionFields, '.');
-    StringRef Kind = SectionFields[3];
-    StringRef DeviceTriple = SectionFields[4];
-    StringRef Arch = SectionFields[5];
-
     StringRef Contents = CDS->getAsString();
-    SmallString<128> TempFile;
-    StringRef DeviceExtension = getDeviceFileExtension(
-        DeviceTriple, identify_magic(Contents) == file_magic::bitcode);
-    if (Error Err = createOutputFile(Prefix + "-" + Kind + "-" + DeviceTriple +
-                                         "-" + Arch,
-                                     DeviceExtension, TempFile))
-      return std::move(Err);
 
-    Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr =
-        FileOutputBuffer::create(TempFile, Contents.size());
-    if (!OutputOrErr)
-      return OutputOrErr.takeError();
-    std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr);
-    std::copy(Contents.begin(), Contents.end(), Output->getBufferStart());
-    if (Error E = Output->commit())
-      return std::move(E);
+    uint64_t Offset = 0;
+    // There could be multiple offloading binaries stored at this section.
+    while (Offset < Contents.size()) {
+      std::unique_ptr<MemoryBuffer> Buffer =
+          MemoryBuffer::getMemBuffer(Contents.drop_front(Offset), GV.getName(),
+                                     /*RequiresNullTerminator*/ false);
+      auto BinaryOrErr = OffloadBinary::create(*Buffer);
+      if (!BinaryOrErr)
+        return BinaryOrErr.takeError();
+      OffloadBinary &Binary = **BinaryOrErr;
+
+      StringRef Kind =
+          getOffloadKindName(static_cast<OffloadKind>(Binary.getOffloadKind()));
+      StringRef Suffix =
+          getImageKindName(static_cast<ImageKind>(Binary.getImageKind()));
+
+      SmallString<128> TempFile;
+      if (Error Err = createOutputFile(Prefix + "-" + Kind + "-" +
+                                           Binary.getTripleStr() + "-" +
+                                           Binary.getArch(),
+                                       Suffix, TempFile))
+        return std::move(Err);
 
-    DeviceFiles.emplace_back(Kind, DeviceTriple, Arch, TempFile);
+      Expected<OffloadFile> File = Binary.createOffloadFile(TempFile);
+      if (!File)
+        return File.takeError();
+
+      DeviceFiles.emplace_back(Kind, File->TheTriple, File->Arch,
+                               File->Filename);
+
+      Offset += Binary.getSize();
+    }
     ToBeDeleted.push_back(&GV);
   }
 
Index: clang/test/Frontend/embed-object.ll
===================================================================
--- clang/test/Frontend/embed-object.ll
+++ clang/test/Frontend/embed-object.ll
@@ -1,11 +1,9 @@
 ; RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm \
-; RUN:    -fembed-offload-object=%S/Inputs/empty.h,section1 \
-; RUN:    -fembed-offload-object=%S/Inputs/empty.h,section2 -x ir %s -o - \
+; RUN:    -fembed-offload-object=file=%S/Inputs/empty.h -x ir %s -o - \
 ; RUN:    | FileCheck %s -check-prefix=CHECK
 
-; CHECK: @[[OBJECT1:.+]] = hidden constant [0 x i8] zeroinitializer, section ".llvm.offloading.section1"
-; CHECK: @[[OBJECT2:.+]] = hidden constant [0 x i8] zeroinitializer, section ".llvm.offloading.section2"
-; CHECK: @llvm.compiler.used = appending global [3 x i8*] [i8* @x, i8* getelementptr inbounds ([0 x i8], [0 x i8]* @[[OBJECT1]], i32 0, i32 0), i8* getelementptr inbounds ([0 x i8], [0 x i8]* @[[OBJECT2]], i32 0, i32 0)], section "llvm.metadata"
+; CHECK: @[[OBJECT:.+]] = private constant [104 x i8] c"\10\FF\10\AD{{.*}}\00", section ".llvm.offloading", align 8
+; CHECK: @llvm.compiler.used = appending global [2 x i8*] [i8* @x, i8* getelementptr inbounds ([104 x i8], [104 x i8]* @[[OBJECT]], i32 0, i32 0)], section "llvm.metadata"
 
 @x = private constant i8 1
 @llvm.compiler.used = appending global [1 x i8*] [i8* @x], section "llvm.metadata"
Index: clang/test/Frontend/embed-object.c
===================================================================
--- clang/test/Frontend/embed-object.c
+++ clang/test/Frontend/embed-object.c
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -x c -triple x86_64-unknown-linux-gnu -emit-llvm -fembed-offload-object=%S/Inputs/empty.h,section
+// RUN: %clang_cc1 -x c -triple x86_64-unknown-linux-gnu -emit-llvm -fembed-offload-object=file=%S/Inputs/empty.h -o - | FileCheck %s
 
-// CHECK: @[[OBJECT:.+]] = private constant [0 x i8] zeroinitializer, section ".llvm.offloading.section"
-// CHECK: @llvm.compiler.used = appending global [3 x i8*] [i8* getelementptr inbounds ([0 x i8], [0 x i8]* @[[OBJECT1]]], section "llvm.metadata"
+// CHECK: @[[OBJECT:.+]] = private constant [104 x i8] c"\10\FF\10\AD\01{{.*}}\00\00", section ".llvm.offloading", align 8
+// CHECK: @llvm.compiler.used = appending global [1 x i8*] [i8* getelementptr inbounds ([104 x i8], [104 x i8]* @[[OBJECT]], i32 0, i32 0)], section "llvm.metadata"
 
 void foo(void) {}
Index: clang/test/Driver/openmp-offload-gpu.c
===================================================================
--- clang/test/Driver/openmp-offload-gpu.c
+++ clang/test/Driver/openmp-offload-gpu.c
@@ -345,4 +345,4 @@
 // RUN:          -fopenmp-new-driver -no-canonical-prefixes -nogpulib %s -o openmp-offload-gpu 2>&1 \
 // RUN:   | FileCheck -check-prefix=NEW_DRIVER_EMBEDDING %s
 
-// NEW_DRIVER_EMBEDDING: -fembed-offload-object=[[CUBIN:.*\.cubin]],openmp.nvptx64-nvidia-cuda.sm_70
+// NEW_DRIVER_EMBEDDING: -fembed-offload-object=file=[[CUBIN:.*\.cubin]],kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
Index: clang/lib/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -7002,13 +7002,12 @@
       const ArgList &TCArgs = C.getArgsForToolChain(TC, "", Action::OFK_OpenMP);
       StringRef File =
           C.getArgs().MakeArgString(TC->getInputFilename(*InputFile));
-      StringRef InputName = Clang::getBaseInputStem(Args, Inputs);
 
       CmdArgs.push_back(Args.MakeArgString(
-          "-fembed-offload-object=" + File + "," +
-          Action::GetOffloadKindName(Action::OFK_OpenMP) + "." +
-          TC->getTripleString() + "." +
-          TCArgs.getLastArgValue(options::OPT_march_EQ) + "." + InputName));
+          "-fembed-offload-object=file=" + File + "," +
+          "kind=" + Action::GetOffloadKindName(Action::OFK_OpenMP) + "," +
+          "triple=" + TC->getTripleString() + "," +
+          "arch=" + TCArgs.getLastArgValue(options::OPT_march_EQ)));
     }
   }
 
Index: clang/lib/CodeGen/BackendUtil.cpp
===================================================================
--- clang/lib/CodeGen/BackendUtil.cpp
+++ clang/lib/CodeGen/BackendUtil.cpp
@@ -10,6 +10,7 @@
 #include "clang/Basic/CodeGenOptions.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/LangOptions.h"
+#include "clang/Basic/Offloading.h"
 #include "clang/Basic/TargetOptions.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Frontend/Utils.h"
@@ -1761,22 +1762,36 @@
     return;
 
   for (StringRef OffloadObject : CGOpts.OffloadObjects) {
-    if (OffloadObject.count(',') != 1)
-      Diags.Report(Diags.getCustomDiagID(
-          DiagnosticsEngine::Error, "Invalid string pair for embedding '%0'"))
-          << OffloadObject;
-    auto FilenameAndSection = OffloadObject.split(',');
-    llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ObjectOrErr =
-        llvm::MemoryBuffer::getFileOrSTDIN(FilenameAndSection.first);
-    if (std::error_code EC = ObjectOrErr.getError()) {
-      auto DiagID = Diags.getCustomDiagID(DiagnosticsEngine::Error,
-                                          "could not open '%0' for embedding");
-      Diags.Report(DiagID) << FilenameAndSection.first;
+    StringMap<StringRef> Fields;
+    SmallVector<StringRef, 4> ObjectFields;
+    OffloadObject.split(ObjectFields, ',');
+
+    for (StringRef Field : ObjectFields) {
+      auto KeyAndValue = Field.split('=');
+      Fields[KeyAndValue.first] = KeyAndValue.second;
+    }
+
+    // Fill the offload file descriptor with the provided metadata. If this gets
+    // any more complicated it should be moved to its own clang tool.
+    OffloadFile File{};
+    File.Filename = Fields["file"].str();
+    File.TheOffloadKind = getOffloadKind(Fields["kind"]);
+    File.TheImageKind = getImageKind(Fields["file"].rsplit(".").second);
+    File.TheTriple = Fields["triple"].str();
+    File.Arch = Fields["arch"].str();
+    File.Features = Fields["features"].str();
+    File.ToolchainPath = Fields["path"].str();
+
+    Expected<std::unique_ptr<MemoryBuffer>> BufferOrErr =
+        OffloadBinary::write(File);
+    if (!BufferOrErr) {
+      auto DiagID = Diags.getCustomDiagID(
+          DiagnosticsEngine::Error, "Could not embed offload object: '%0'");
+      Diags.Report(DiagID) << BufferOrErr.takeError();
       return;
     }
 
-    SmallString<128> SectionName(
-        {".llvm.offloading.", FilenameAndSection.second});
-    llvm::embedBufferInModule(*M, **ObjectOrErr, SectionName);
+    llvm::embedBufferInModule(*M, **BufferOrErr, ".llvm.offloading",
+                              Align(OffloadBinary::getAlignment()));
   }
 }
Index: clang/lib/Basic/Offloading.cpp
===================================================================
--- /dev/null
+++ clang/lib/Basic/Offloading.cpp
@@ -0,0 +1,166 @@
+//===- Offloading.cpp - Utilities for handling offloading code  -*- C++ -*-===//
+//
+// 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 "clang/Basic/Offloading.h"
+
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/MC/StringTableBuilder.h"
+#include "llvm/Support/FileOutputBuffer.h"
+
+using namespace llvm;
+
+namespace clang {
+
+Expected<std::unique_ptr<OffloadBinary>>
+OffloadBinary::create(MemoryBufferRef Buffer) {
+  if (Buffer.getBufferSize() < sizeof(Header) + sizeof(Entry))
+    return createStringError(inconvertibleErrorCode(), "Invalid size");
+
+  // Check for 0x10FF1OAD magic bytes.
+  if (!Buffer.getBuffer().startswith("\x10\xff\x10\xad"))
+    return createStringError(inconvertibleErrorCode(),
+                             "Offloading section missing magic bytes");
+
+  const char *Start = Buffer.getBufferStart();
+  const Header *TheHeader = reinterpret_cast<const Header *>(Start);
+  const Entry *TheEntry =
+      reinterpret_cast<const Entry *>(&Start[TheHeader->EntryOffset]);
+
+  return std::unique_ptr<OffloadBinary>(
+      new OffloadBinary(Buffer, TheHeader, TheEntry));
+}
+
+Expected<std::unique_ptr<MemoryBuffer>>
+OffloadBinary::write(OffloadFile &File) {
+  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ImageOrErr =
+      llvm::MemoryBuffer::getFileOrSTDIN(File.Filename);
+  if (std::error_code EC = ImageOrErr.getError())
+    return createFileError(File.Filename, EC);
+
+  MemoryBufferRef Image = **ImageOrErr;
+
+  // Create a string table with all the used strings.
+  StringTableBuilder StrTab(StringTableBuilder::ELF);
+  StrTab.add(File.TheTriple);
+  StrTab.add(File.Arch);
+  StrTab.add(File.Features);
+  StrTab.add(File.ToolchainPath);
+  StrTab.finalize();
+
+  // Create the header and fill in the offsets. The entry will be directly
+  // placed after the header in memory. Align the size to the alignment of the
+  // header so this can be placed contiguously in a single section.
+  Header TheHeader;
+  TheHeader.Size = alignTo(sizeof(Header) + sizeof(Entry) +
+                               Image.getBufferSize() + StrTab.getSize(),
+                           alignof(Header));
+  TheHeader.EntryOffset = sizeof(Header);
+  TheHeader.EntrySize = sizeof(Entry);
+
+  // Create the entry using the string table offsets. The string table will be
+  // placed directly after the entry in memory, and the image after that.
+  Entry TheEntry;
+  TheEntry.ImageKind = File.TheImageKind;
+  TheEntry.OffloadKind = File.TheOffloadKind;
+  TheEntry.Flags = File.Flags;
+
+  TheEntry.TripleOffset =
+      sizeof(Header) + sizeof(Entry) + StrTab.getOffset(File.TheTriple);
+  TheEntry.ArchOffset =
+      sizeof(Header) + sizeof(Entry) + StrTab.getOffset(File.Arch);
+  TheEntry.FeaturesOffset =
+      sizeof(Header) + sizeof(Entry) + StrTab.getOffset(File.Features);
+  TheEntry.PathOffset =
+      sizeof(Header) + sizeof(Entry) + StrTab.getOffset(File.ToolchainPath);
+  TheEntry.ImageOffset = sizeof(Header) + sizeof(Entry) + StrTab.getSize();
+  TheEntry.ImageSize = Image.getBufferSize();
+
+  SmallVector<char, 256> Data;
+  raw_svector_ostream OS(Data);
+  OS << StringRef(reinterpret_cast<char *>(&TheHeader), sizeof(Header));
+  OS << StringRef(reinterpret_cast<char *>(&TheEntry), sizeof(Entry));
+  StrTab.write(OS);
+  OS << Image.getBuffer();
+
+  // Add final padding to required alignment.
+  assert(TheHeader.Size >= OS.tell() && "Too much data written?");
+  OS.write_zeros(TheHeader.Size - OS.tell());
+  assert(TheHeader.Size == OS.tell() && "Size mismatch");
+
+  return MemoryBuffer::getMemBufferCopy(OS.str());
+}
+
+Expected<OffloadFile> OffloadBinary::createOffloadFile(StringRef Filename) {
+  ImageKind TheImageKind = static_cast<ImageKind>(getImageKind());
+  OffloadKind TheOffloadKind = static_cast<OffloadKind>(getOffloadKind());
+
+  // Write the device image data to the provided filename.
+  Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr =
+      FileOutputBuffer::create(Filename, getImage().size());
+  if (!OutputOrErr)
+    return OutputOrErr.takeError();
+  std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr);
+  std::copy(getImage().bytes_begin(), getImage().bytes_end(),
+            Output->getBufferStart());
+  if (Error E = Output->commit())
+    return std::move(E);
+
+  return OffloadFile{TheImageKind,         TheOffloadKind,  getFlags(),
+                     getTripleStr().str(), getArch().str(), getFeatures().str(),
+                     getPath().str(),      Filename.str()};
+}
+
+OffloadKind getOffloadKind(StringRef Name) {
+  return llvm::StringSwitch<OffloadKind>(Name)
+      .Case("openmp", OFK_OpenMP)
+      .Case("cuda", OFK_Cuda)
+      .Case("hip", OFK_HIP)
+      .Default(OFK_None);
+}
+
+StringRef getOffloadKindName(OffloadKind Kind) {
+  switch (Kind) {
+  case OFK_OpenMP:
+    return "openmp";
+  case OFK_Cuda:
+    return "cuda";
+  case OFK_HIP:
+    return "hip";
+  default:
+    return "none";
+  }
+}
+
+ImageKind getImageKind(StringRef Name) {
+  return llvm::StringSwitch<ImageKind>(Name)
+      .Case("o", IMG_Object)
+      .Case("bc", IMG_Bitcode)
+      .Case("cubin", IMG_Cubin)
+      .Case("fatbin", IMG_Fatbinary)
+      .Case("s", IMG_PTX)
+      .Default(IMG_None);
+}
+
+StringRef getImageKindName(ImageKind Kind) {
+  switch (Kind) {
+  case IMG_Object:
+    return "o";
+  case IMG_Bitcode:
+    return "bc";
+  case IMG_Cubin:
+    return "cubin";
+  case IMG_Fatbinary:
+    return "fatbin";
+  case IMG_PTX:
+    return "s";
+  default:
+    return "";
+  }
+}
+
+} // namespace clang
Index: clang/lib/Basic/CMakeLists.txt
===================================================================
--- clang/lib/Basic/CMakeLists.txt
+++ clang/lib/Basic/CMakeLists.txt
@@ -1,5 +1,6 @@
 set(LLVM_LINK_COMPONENTS
   Support
+  MC
   )
 
 find_first_existing_vc_file("${LLVM_MAIN_SRC_DIR}" llvm_vc)
@@ -43,6 +44,7 @@
   CharInfo.cpp
   CodeGenOptions.cpp
   Cuda.cpp
+  Offloading.cpp
   DarwinSDKInfo.cpp
   Diagnostic.cpp
   DiagnosticIDs.cpp
Index: clang/include/clang/Basic/Offloading.h
===================================================================
--- /dev/null
+++ clang/include/clang/Basic/Offloading.h
@@ -0,0 +1,146 @@
+//===--- Offloading.h - Utilities for handling offloading code  -*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_BASIC_OFFLOADING_H
+#define LLVM_CLANG_BASIC_OFFLOADING_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <memory>
+
+using llvm::Expected;
+using llvm::MemoryBuffer;
+using llvm::MemoryBufferRef;
+using llvm::StringRef;
+using llvm::Twine;
+
+namespace clang {
+
+/// The producer of the associated offloading image.
+enum OffloadKind : uint16_t {
+  OFK_None = 0,
+  OFK_OpenMP,
+  OFK_Cuda,
+  OFK_HIP,
+};
+
+/// The type of contents the offloading image contains.
+enum ImageKind : uint16_t {
+  IMG_None = 0,
+  IMG_Object,
+  IMG_Bitcode,
+  IMG_Cubin,
+  IMG_Fatbinary,
+  IMG_PTX,
+};
+
+/// A generic struct for metadata associated with an offloading file. We need to
+/// associate metadata with an offloading file so we can properly identify it
+/// during the linking phase.
+struct OffloadFile {
+  ImageKind TheImageKind;
+  OffloadKind TheOffloadKind;
+  uint32_t Flags;
+
+  std::string TheTriple; // The target triple associated with this image.
+  std::string Arch;      // The target architecture associated with this image.
+  std::string Features;  // Associated target features.
+  std::string ToolchainPath; // Installation path for the toolchain.
+  std::string Filename;      // The filename containing the image data.
+};
+
+/// A simple binary serialization of an offloading file. We use this format to
+/// embed the offloading image into the host executable so it can be extracted
+/// and used by the linker.
+///
+/// Many of these could be stored in the same section by the time the linker
+/// sees it so we mark this information with a header. The version is used to
+/// detect ABI stability and the size is used to find other offloading entries
+/// that may exist in the same section. All offsets are given as absolute byte
+/// offsets from the beginning of the file.
+class OffloadBinary {
+public:
+  /// Attempt to parse the offloading binary stored in \p Data.
+  static Expected<std::unique_ptr<OffloadBinary>> create(MemoryBufferRef Data);
+
+  /// Serialize the contents of \p File to a binary buffer to be read later.
+  static Expected<std::unique_ptr<MemoryBuffer>> write(OffloadFile &File);
+
+  static uint64_t getAlignment() { return alignof(Header); }
+
+  uint16_t getImageKind() const { return TheEntry->ImageKind; }
+  uint16_t getOffloadKind() const { return TheEntry->OffloadKind; }
+  uint32_t getFlags() const { return TheEntry->Flags; }
+  uint64_t getSize() const { return TheHeader->Size; }
+
+  StringRef getTripleStr() const { return &Data[TheEntry->TripleOffset]; }
+  StringRef getArch() const { return &Data[TheEntry->ArchOffset]; }
+  StringRef getFeatures() const { return &Data[TheEntry->FeaturesOffset]; }
+  StringRef getPath() const { return &Data[TheEntry->PathOffset]; }
+  StringRef getImage() const {
+    return StringRef(&Data[TheEntry->ImageOffset], TheEntry->ImageSize);
+  }
+
+  /// Convert the binary file to an offload file and write the image to the
+  /// associated \p Filename.
+  Expected<OffloadFile> createOffloadFile(StringRef Filename);
+
+private:
+  struct Header {
+    uint8_t Magic[4] = {0x10, 0xFF, 0x10, 0xAD}; // 0x10FF10AD magic bytes.
+    uint32_t Version = 1;                        // Version identifier.
+    uint64_t Size;        // Size in bytes of this entire binary.
+    uint64_t EntryOffset; // Offset of the metadata entry in bytes.
+    uint64_t EntrySize;   // Size of the metadata entry in bytes.
+  };
+
+  struct Entry {
+    uint16_t ImageKind;      // The kind of the image stored.
+    uint16_t OffloadKind;    // The producer of this image.
+    uint32_t Flags;          // Additional flags associated with the image.
+    uint64_t TripleOffset;   // Offset in bytes of the triple string.
+    uint64_t ArchOffset;     // Offset in bytes of the subarchitecture string.
+    uint64_t FeaturesOffset; // Offset in bytes of the features.
+    uint64_t PathOffset;     // Offset in bytes of the toolchain path.
+    uint64_t Reserved;       // Reserved data for future use.
+    uint64_t ImageOffset;    // Offset in bytes of the actual binary image.
+    uint64_t ImageSize;      // Size in bytes of the binary image.
+  };
+
+  OffloadBinary(MemoryBufferRef Buffer, const Header *TheHeader,
+                const Entry *TheEntry)
+      : Buffer(Buffer), Data(Buffer.getBufferStart()), TheHeader(TheHeader),
+        TheEntry(TheEntry) {}
+
+  OffloadBinary(const OffloadBinary &Other) = delete;
+
+  MemoryBufferRef Buffer;
+
+  /// Pointer to the beginning of the memory buffer for convenience.
+  const char *Data;
+  /// Location of the header within the binary.
+  const Header *TheHeader;
+  /// Location of the metadata entries within the binary.
+  const Entry *TheEntry;
+};
+
+/// Convert a string \p Name to an image kind.
+ImageKind getImageKind(StringRef Name);
+
+/// Convert an image kind to its string representation.
+StringRef getImageKindName(ImageKind Name);
+
+/// Convert a string \p Name to an offload kind.
+OffloadKind getOffloadKind(StringRef Name);
+
+/// Convert an offload kind to its string representation.
+StringRef getOffloadKindName(OffloadKind Name);
+
+} // namespace clang
+#endif
Index: clang/include/clang/Basic/CodeGenOptions.h
===================================================================
--- clang/include/clang/Basic/CodeGenOptions.h
+++ clang/include/clang/Basic/CodeGenOptions.h
@@ -276,9 +276,9 @@
   /// CUDA runtime back-end for incorporating them into host-side object file.
   std::string CudaGpuBinaryFileName;
 
-  /// List of filenames and section name pairs passed in using the
-  /// -fembed-offload-object option to embed device-side offloading objects into
-  /// the host as a named section. Input passed in as '<filename>,<section>'
+  /// List of filenames and metadata passed in using the -fembed-offload-object
+  /// option to embed device-side offloading objects into the host as a named
+  /// section. Input passed in as 'file=<filename>,<key>=<metadata>, ...'
   std::vector<std::string> OffloadObjects;
 
   /// The name of the file to which the backend should save YAML optimization
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to