jhuber6 created this revision.
jhuber6 added reviewers: jdoerfert, JonChesterfield.
Herald added subscribers: ormris, dexonsmith, hiraditya, mgorny.
Herald added a project: All.
jhuber6 requested review of this revision.
Herald added subscribers: llvm-commits, cfe-commits, sstefan1.
Herald added projects: clang, LLVM.

We need to embed certain metadata along with a binary image when we wish
to perform a device-linking job on it. Currently this metadata was
embedded in the section name of the data itself. This worked, but made
adding new metadata very difficult and didn't work if the user did any
sort of section linking.

This patch introduces a custom binary format for bundling offloading
metadata with a device object file. This binary format is fundamentally
a simple string table with some additional data and an embedded image. I
decided to use a custom format rather than using an existing format
(ELF, JSON, etc) because of the specialty use-case of this.

This extension will make it easier to extend the linker wrapper's
capabilties with whatever data is necessary. Eventually this will allow
us to remove all the external arguments passed to the linker wrapper and
embed it directly in the host's linker so device linking behaves exactly
like host linking.


Repository:
  rG LLVM Github Monorepo

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/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
@@ -266,14 +266,13 @@
 
 void llvm::embedBufferInModule(Module &M, MemoryBufferRef Buf,
                                StringRef SectionName) {
-  // Embed the buffer into the module.
+  // 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);
 
   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: 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,
@@ -296,39 +299,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 +419,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);
+
+      Expected<OffloadFile> File = Binary.createOffloadFile(TempFile);
+      if (!File)
+        return File.takeError();
 
-    DeviceFiles.emplace_back(Kind, DeviceTriple, Arch, TempFile);
+      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 [88 x i8] c"\10\FF\10\AD{{.*}}\00", section ".llvm.offloading"
+; CHECK: @llvm.compiler.used = appending global [2 x i8*] [i8* @x, i8* getelementptr inbounds ([88 x i8], [88 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 [88 x i8] c"\10\FF\10\AD\01{{.*}}\00\00", section ".llvm.offloading"
+// CHECK: @llvm.compiler.used = appending global [1 x i8*] [i8* getelementptr inbounds ([88 x i8], [88 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
@@ -6974,13 +6974,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,35 @@
     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.CmdLine = Fields["cmdline"].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");
   }
 }
Index: clang/lib/Basic/Offloading.cpp
===================================================================
--- /dev/null
+++ clang/lib/Basic/Offloading.cpp
@@ -0,0 +1,164 @@
+//===- 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(), "Invalid signature");
+
+  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.CmdLine);
+  StrTab.finalize();
+
+  // Create the header and fill in the offsets. The entry will be directly
+  // placed after the header in memory. Align the total size to the alignment of
+  // the header so they can be placed contiguously in a single section.
+  Header TheHeader;
+  TheHeader.Version = 1;
+  TheHeader.Size = alignTo(sizeof(Header) + sizeof(Entry) +
+                               Image.getBufferSize() + StrTab.getSize(),
+                           alignof(Header));
+  TheHeader.EntryOffset = sizeof(Header);
+
+  // 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.CmdLineOffset =
+      sizeof(Header) + sizeof(Entry) + StrTab.getOffset(File.CmdLine);
+  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());
+
+  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(),
+                     getCmdLine().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,128 @@
+//===--- 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 offloading action associated with the 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 CmdLine;   // Associated command line arguments (e.g. cuda-path).
+  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.
+class OffloadBinary {
+public:
+  static Expected<std::unique_ptr<OffloadBinary>> create(MemoryBufferRef Data);
+
+  static Expected<std::unique_ptr<MemoryBuffer>> write(OffloadFile &File);
+
+  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 getCmdLine() const { return &Data[TheEntry->CmdLineOffset]; }
+  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};
+    uint32_t Version;
+    uint64_t Size;
+    uint64_t EntryOffset;
+  };
+
+  struct Entry {
+    uint16_t ImageKind;
+    uint16_t OffloadKind;
+    uint32_t Flags;
+    uint64_t TripleOffset;
+    uint64_t ArchOffset;
+    uint64_t FeaturesOffset;
+    uint64_t CmdLineOffset;
+    uint64_t ImageOffset;
+    uint64_t ImageSize;
+  };
+
+  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.
+  const char *Data;
+  /// Location of the header within the binary.
+  const Header *TheHeader;
+  /// Location of the metadata entries within the binary.
+  const Entry *TheEntry;
+};
+
+ImageKind getImageKind(StringRef Name);
+StringRef getImageKindName(ImageKind Name);
+
+OffloadKind getOffloadKind(StringRef Name);
+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