diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/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 ',
' + /// 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=,=, ...' std::vector OffloadObjects; /// The name of the file to which the backend should save YAML optimization diff --git a/clang/include/clang/Basic/Offloading.h b/clang/include/clang/Basic/Offloading.h new file mode 100644 --- /dev/null +++ b/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 + +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> create(MemoryBufferRef Data); + + static Expected> 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 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 diff --git a/clang/lib/Basic/CMakeLists.txt b/clang/lib/Basic/CMakeLists.txt --- a/clang/lib/Basic/CMakeLists.txt +++ b/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 diff --git a/clang/lib/Basic/Offloading.cpp b/clang/lib/Basic/Offloading.cpp new file mode 100644 --- /dev/null +++ b/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> +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(Start); + const Entry *TheEntry = + reinterpret_cast(&Start[TheHeader->EntryOffset]); + + return std::unique_ptr( + new OffloadBinary(Buffer, TheHeader, TheEntry)); +} + +Expected> +OffloadBinary::write(OffloadFile &File) { + llvm::ErrorOr> 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 Data; + raw_svector_ostream OS(Data); + OS << StringRef(reinterpret_cast(&TheHeader), sizeof(Header)); + OS << StringRef(reinterpret_cast(&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 OffloadBinary::createOffloadFile(StringRef Filename) { + ImageKind TheImageKind = static_cast(getImageKind()); + OffloadKind TheOffloadKind = static_cast(getOffloadKind()); + + // Write the device image data to the provided filename. + Expected> OutputOrErr = + FileOutputBuffer::create(Filename, getImage().size()); + if (!OutputOrErr) + return OutputOrErr.takeError(); + std::unique_ptr 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(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(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 diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/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> 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 Fields; + SmallVector 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> 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"); } } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/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))); } } diff --git a/clang/test/Driver/openmp-offload-gpu.c b/clang/test/Driver/openmp-offload-gpu.c --- a/clang/test/Driver/openmp-offload-gpu.c +++ b/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 diff --git a/clang/test/Frontend/embed-object.c b/clang/test/Frontend/embed-object.c --- a/clang/test/Frontend/embed-object.c +++ b/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) {} diff --git a/clang/test/Frontend/embed-object.ll b/clang/test/Frontend/embed-object.ll --- a/clang/test/Frontend/embed-object.ll +++ b/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" diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/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 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..`. -#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 ToBeStripped; - // Extract data from sections of the form `.llvm.offloading..`. + // Extract offloading binaries from sections with the name `.llvm.offloading`. for (const SectionRef &Sec : Obj.sections()) { Expected Name = Sec.getName(); - if (!Name || !Name->startswith(OFFLOAD_SECTION_MAGIC_STR)) + if (!Name || !Name->equals(OFFLOAD_SECTION_MAGIC_STR)) continue; - SmallVector SectionFields; - Name->split(SectionFields, '.'); - StringRef Kind = SectionFields[3]; - StringRef DeviceTriple = SectionFields[4]; - StringRef Arch = SectionFields[5]; + Expected 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 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(Binary.getOffloadKind())); + StringRef Suffix = + getImageKindName(static_cast(Binary.getImageKind())); - if (Expected 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> OutputOrErr = - FileOutputBuffer::create(TempFile, Sec.getSize()); - if (!OutputOrErr) - return OutputOrErr.takeError(); - std::unique_ptr 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 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 ToBeDeleted; - // Extract data from the global string containing a section of the form - // `.llvm.offloading..`. + // 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(GV.getInitializer()); if (!CDS) continue; - SmallVector 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> OutputOrErr = - FileOutputBuffer::create(TempFile, Contents.size()); - if (!OutputOrErr) - return OutputOrErr.takeError(); - std::unique_ptr 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 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(Binary.getOffloadKind())); + StringRef Suffix = + getImageKindName(static_cast(Binary.getImageKind())); + + SmallString<128> TempFile; + if (Error Err = createOutputFile(Prefix + "-" + Kind + "-" + + Binary.getTripleStr() + "-" + + Binary.getArch(), + Suffix, TempFile)) + return std::move(Err); + + Expected 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); } diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/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; diff --git a/llvm/lib/Transforms/Utils/ModuleUtils.cpp b/llvm/lib/Transforms/Utils/ModuleUtils.cpp --- a/llvm/lib/Transforms/Utils/ModuleUtils.cpp +++ b/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); }