xref: /freebsd/contrib/llvm-project/llvm/include/llvm/Object/OffloadBinary.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
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