1 //===--- Offloading.h - Utilities for handling offloading code -*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file contains the binary format used for budingling device metadata with 10 // an associated device image. The data can then be stored inside a host object 11 // file to create a fat binary and read by the linker. This is intended to be a 12 // thin wrapper around the image itself. If this format becomes sufficiently 13 // complex it should be moved to a standard binary format like msgpack or ELF. 14 // 15 //===----------------------------------------------------------------------===// 16 17 #ifndef LLVM_OBJECT_OFFLOADBINARY_H 18 #define LLVM_OBJECT_OFFLOADBINARY_H 19 20 #include "llvm/ADT/MapVector.h" 21 #include "llvm/ADT/SmallString.h" 22 #include "llvm/ADT/StringRef.h" 23 #include "llvm/Object/Binary.h" 24 #include "llvm/Support/Compiler.h" 25 #include "llvm/Support/Error.h" 26 #include "llvm/Support/MemoryBuffer.h" 27 #include <memory> 28 29 namespace llvm { 30 31 namespace object { 32 33 /// The producer of the associated offloading image. 34 enum OffloadKind : uint16_t { 35 OFK_None = 0, 36 OFK_OpenMP = (1 << 0), 37 OFK_Cuda = (1 << 1), 38 OFK_HIP = (1 << 2), 39 OFK_SYCL = (1 << 3), 40 OFK_LAST = (1 << 4), 41 }; 42 43 /// The type of contents the offloading image contains. 44 enum ImageKind : uint16_t { 45 IMG_None = 0, 46 IMG_Object, 47 IMG_Bitcode, 48 IMG_Cubin, 49 IMG_Fatbinary, 50 IMG_PTX, 51 IMG_LAST, 52 }; 53 54 /// A simple binary serialization of an offloading file. We use this format to 55 /// embed the offloading image into the host executable so it can be extracted 56 /// and used by the linker. 57 /// 58 /// Many of these could be stored in the same section by the time the linker 59 /// sees it so we mark this information with a header. The version is used to 60 /// detect ABI stability and the size is used to find other offloading entries 61 /// that may exist in the same section. All offsets are given as absolute byte 62 /// offsets from the beginning of the file. 63 class OffloadBinary : public Binary { 64 public: 65 using string_iterator = MapVector<StringRef, StringRef>::const_iterator; 66 using string_iterator_range = iterator_range<string_iterator>; 67 68 /// The current version of the binary used for backwards compatibility. 69 static const uint32_t Version = 1; 70 71 /// The offloading metadata that will be serialized to a memory buffer. 72 struct OffloadingImage { 73 ImageKind TheImageKind; 74 OffloadKind TheOffloadKind; 75 uint32_t Flags; 76 MapVector<StringRef, StringRef> StringData; 77 std::unique_ptr<MemoryBuffer> Image; 78 }; 79 80 /// Attempt to parse the offloading binary stored in \p Data. 81 LLVM_ABI static Expected<std::unique_ptr<OffloadBinary>> 82 create(MemoryBufferRef); 83 84 /// Serialize the contents of \p File to a binary buffer to be read later. 85 LLVM_ABI static SmallString<0> write(const OffloadingImage &); 86 getAlignment()87 static uint64_t getAlignment() { return 8; } 88 getImageKind()89 ImageKind getImageKind() const { return TheEntry->TheImageKind; } getOffloadKind()90 OffloadKind getOffloadKind() const { return TheEntry->TheOffloadKind; } getVersion()91 uint32_t getVersion() const { return TheHeader->Version; } getFlags()92 uint32_t getFlags() const { return TheEntry->Flags; } getSize()93 uint64_t getSize() const { return TheHeader->Size; } 94 getTriple()95 StringRef getTriple() const { return getString("triple"); } getArch()96 StringRef getArch() const { return getString("arch"); } getImage()97 StringRef getImage() const { 98 return StringRef(&Buffer[TheEntry->ImageOffset], TheEntry->ImageSize); 99 } 100 101 // Iterator over all the key and value pairs in the binary. strings()102 string_iterator_range strings() const { 103 return string_iterator_range(StringData.begin(), StringData.end()); 104 } 105 getString(StringRef Key)106 StringRef getString(StringRef Key) const { return StringData.lookup(Key); } 107 classof(const Binary * V)108 static bool classof(const Binary *V) { return V->isOffloadFile(); } 109 110 struct Header { 111 uint8_t Magic[4] = {0x10, 0xFF, 0x10, 0xAD}; // 0x10FF10AD magic bytes. 112 uint32_t Version = OffloadBinary::Version; // Version identifier. 113 uint64_t Size; // Size in bytes of this entire binary. 114 uint64_t EntryOffset; // Offset of the metadata entry in bytes. 115 uint64_t EntrySize; // Size of the metadata entry in bytes. 116 }; 117 118 struct Entry { 119 ImageKind TheImageKind; // The kind of the image stored. 120 OffloadKind TheOffloadKind; // The producer of this image. 121 uint32_t Flags; // Additional flags associated with the image. 122 uint64_t StringOffset; // Offset in bytes to the string map. 123 uint64_t NumStrings; // Number of entries in the string map. 124 uint64_t ImageOffset; // Offset in bytes of the actual binary image. 125 uint64_t ImageSize; // Size in bytes of the binary image. 126 }; 127 128 struct StringEntry { 129 uint64_t KeyOffset; 130 uint64_t ValueOffset; 131 }; 132 133 private: OffloadBinary(MemoryBufferRef Source,const Header * TheHeader,const Entry * TheEntry)134 OffloadBinary(MemoryBufferRef Source, const Header *TheHeader, 135 const Entry *TheEntry) 136 : Binary(Binary::ID_Offload, Source), Buffer(Source.getBufferStart()), 137 TheHeader(TheHeader), TheEntry(TheEntry) { 138 const StringEntry *StringMapBegin = 139 reinterpret_cast<const StringEntry *>(&Buffer[TheEntry->StringOffset]); 140 for (uint64_t I = 0, E = TheEntry->NumStrings; I != E; ++I) { 141 StringRef Key = &Buffer[StringMapBegin[I].KeyOffset]; 142 StringData[Key] = &Buffer[StringMapBegin[I].ValueOffset]; 143 } 144 } 145 146 OffloadBinary(const OffloadBinary &Other) = delete; 147 148 /// Map from keys to offsets in the binary. 149 MapVector<StringRef, StringRef> StringData; 150 /// Raw pointer to the MemoryBufferRef for convenience. 151 const char *Buffer; 152 /// Location of the header within the binary. 153 const Header *TheHeader; 154 /// Location of the metadata entries within the binary. 155 const Entry *TheEntry; 156 }; 157 158 /// A class to contain the binary information for a single OffloadBinary that 159 /// owns its memory. 160 class OffloadFile : public OwningBinary<OffloadBinary> { 161 public: 162 using TargetID = std::pair<StringRef, StringRef>; 163 OffloadFile(std::unique_ptr<OffloadBinary> Binary,std::unique_ptr<MemoryBuffer> Buffer)164 OffloadFile(std::unique_ptr<OffloadBinary> Binary, 165 std::unique_ptr<MemoryBuffer> Buffer) 166 : OwningBinary<OffloadBinary>(std::move(Binary), std::move(Buffer)) {} 167 168 /// Make a deep copy of this offloading file. copy()169 OffloadFile copy() const { 170 std::unique_ptr<MemoryBuffer> Buffer = MemoryBuffer::getMemBufferCopy( 171 getBinary()->getMemoryBufferRef().getBuffer(), 172 getBinary()->getMemoryBufferRef().getBufferIdentifier()); 173 174 // This parsing should never fail because it has already been parsed. 175 auto NewBinaryOrErr = OffloadBinary::create(*Buffer); 176 assert(NewBinaryOrErr && "Failed to parse a copy of the binary?"); 177 if (!NewBinaryOrErr) 178 llvm::consumeError(NewBinaryOrErr.takeError()); 179 return OffloadFile(std::move(*NewBinaryOrErr), std::move(Buffer)); 180 } 181 182 /// We use the Triple and Architecture pair to group linker inputs together. 183 /// This conversion function lets us use these inputs in a hash-map. TargetID()184 operator TargetID() const { 185 return std::make_pair(getBinary()->getTriple(), getBinary()->getArch()); 186 } 187 }; 188 189 /// Extracts embedded device offloading code from a memory \p Buffer to a list 190 /// of \p Binaries. 191 LLVM_ABI Error extractOffloadBinaries(MemoryBufferRef Buffer, 192 SmallVectorImpl<OffloadFile> &Binaries); 193 194 /// Convert a string \p Name to an image kind. 195 LLVM_ABI ImageKind getImageKind(StringRef Name); 196 197 /// Convert an image kind to its string representation. 198 LLVM_ABI StringRef getImageKindName(ImageKind Name); 199 200 /// Convert a string \p Name to an offload kind. 201 LLVM_ABI OffloadKind getOffloadKind(StringRef Name); 202 203 /// Convert an offload kind to its string representation. 204 LLVM_ABI StringRef getOffloadKindName(OffloadKind Name); 205 206 /// If the target is AMD we check the target IDs for mutual compatibility. A 207 /// target id is a string conforming to the folowing BNF syntax: 208 /// 209 /// target-id ::= '<arch> ( : <feature> ( '+' | '-' ) )*' 210 /// 211 /// The features 'xnack' and 'sramecc' are currently supported. These can be in 212 /// the state of on, off, and any when unspecified. A target marked as any can 213 /// bind with either on or off. This is used to link mutually compatible 214 /// architectures together. Returns false in the case of an exact match. 215 LLVM_ABI bool areTargetsCompatible(const OffloadFile::TargetID &LHS, 216 const OffloadFile::TargetID &RHS); 217 218 } // namespace object 219 220 } // namespace llvm 221 #endif 222