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/StringMap.h"
21 #include "llvm/ADT/StringRef.h"
22 #include "llvm/Object/Binary.h"
23 #include "llvm/Support/Error.h"
24 #include "llvm/Support/MemoryBuffer.h"
25 #include <memory>
26 
27 namespace llvm {
28 
29 namespace object {
30 
31 /// The producer of the associated offloading image.
32 enum OffloadKind : uint16_t {
33   OFK_None = 0,
34   OFK_OpenMP,
35   OFK_Cuda,
36   OFK_HIP,
37   OFK_LAST,
38 };
39 
40 /// The type of contents the offloading image contains.
41 enum ImageKind : uint16_t {
42   IMG_None = 0,
43   IMG_Object,
44   IMG_Bitcode,
45   IMG_Cubin,
46   IMG_Fatbinary,
47   IMG_PTX,
48   IMG_LAST,
49 };
50 
51 /// A simple binary serialization of an offloading file. We use this format to
52 /// embed the offloading image into the host executable so it can be extracted
53 /// and used by the linker.
54 ///
55 /// Many of these could be stored in the same section by the time the linker
56 /// sees it so we mark this information with a header. The version is used to
57 /// detect ABI stability and the size is used to find other offloading entries
58 /// that may exist in the same section. All offsets are given as absolute byte
59 /// offsets from the beginning of the file.
60 class OffloadBinary : public Binary {
61 public:
62   using string_iterator = StringMap<StringRef>::const_iterator;
63   using string_iterator_range = iterator_range<string_iterator>;
64 
65   /// The current version of the binary used for backwards compatibility.
66   static const uint32_t Version = 1;
67 
68   /// The offloading metadata that will be serialized to a memory buffer.
69   struct OffloadingImage {
70     ImageKind TheImageKind;
71     OffloadKind TheOffloadKind;
72     uint32_t Flags;
73     StringMap<StringRef> StringData;
74     std::unique_ptr<MemoryBuffer> Image;
75   };
76 
77   /// Attempt to parse the offloading binary stored in \p Data.
78   static Expected<std::unique_ptr<OffloadBinary>> create(MemoryBufferRef);
79 
80   /// Serialize the contents of \p File to a binary buffer to be read later.
81   static std::unique_ptr<MemoryBuffer> write(const OffloadingImage &);
82 
83   static uint64_t getAlignment() { return 8; }
84 
85   ImageKind getImageKind() const { return TheEntry->TheImageKind; }
86   OffloadKind getOffloadKind() const { return TheEntry->TheOffloadKind; }
87   uint32_t getVersion() const { return TheHeader->Version; }
88   uint32_t getFlags() const { return TheEntry->Flags; }
89   uint64_t getSize() const { return TheHeader->Size; }
90 
91   StringRef getTriple() const { return getString("triple"); }
92   StringRef getArch() const { return getString("arch"); }
93   StringRef getImage() const {
94     return StringRef(&Buffer[TheEntry->ImageOffset], TheEntry->ImageSize);
95   }
96 
97   // Iterator over all the key and value pairs in the binary.
98   string_iterator_range strings() const {
99     return string_iterator_range(StringData.begin(), StringData.end());
100   }
101 
102   StringRef getString(StringRef Key) const { return StringData.lookup(Key); }
103 
104   static bool classof(const Binary *V) { return V->isOffloadFile(); }
105 
106   struct Header {
107     uint8_t Magic[4] = {0x10, 0xFF, 0x10, 0xAD}; // 0x10FF10AD magic bytes.
108     uint32_t Version = OffloadBinary::Version;   // Version identifier.
109     uint64_t Size;        // Size in bytes of this entire binary.
110     uint64_t EntryOffset; // Offset of the metadata entry in bytes.
111     uint64_t EntrySize;   // Size of the metadata entry in bytes.
112   };
113 
114   struct Entry {
115     ImageKind TheImageKind;     // The kind of the image stored.
116     OffloadKind TheOffloadKind; // The producer of this image.
117     uint32_t Flags;             // Additional flags associated with the image.
118     uint64_t StringOffset;      // Offset in bytes to the string map.
119     uint64_t NumStrings;        // Number of entries in the string map.
120     uint64_t ImageOffset;       // Offset in bytes of the actual binary image.
121     uint64_t ImageSize;         // Size in bytes of the binary image.
122   };
123 
124   struct StringEntry {
125     uint64_t KeyOffset;
126     uint64_t ValueOffset;
127   };
128 
129 private:
130   OffloadBinary(MemoryBufferRef Source, const Header *TheHeader,
131                 const Entry *TheEntry)
132       : Binary(Binary::ID_Offload, Source), Buffer(Source.getBufferStart()),
133         TheHeader(TheHeader), TheEntry(TheEntry) {
134     const StringEntry *StringMapBegin =
135         reinterpret_cast<const StringEntry *>(&Buffer[TheEntry->StringOffset]);
136     for (uint64_t I = 0, E = TheEntry->NumStrings; I != E; ++I) {
137       StringRef Key = &Buffer[StringMapBegin[I].KeyOffset];
138       StringData[Key] = &Buffer[StringMapBegin[I].ValueOffset];
139     }
140   }
141 
142   OffloadBinary(const OffloadBinary &Other) = delete;
143 
144   /// Map from keys to offsets in the binary.
145   StringMap<StringRef> StringData;
146   /// Raw pointer to the MemoryBufferRef for convenience.
147   const char *Buffer;
148   /// Location of the header within the binary.
149   const Header *TheHeader;
150   /// Location of the metadata entries within the binary.
151   const Entry *TheEntry;
152 };
153 
154 /// A class to contain the binary information for a single OffloadBinary that
155 /// owns its memory.
156 class OffloadFile : public OwningBinary<OffloadBinary> {
157 public:
158   using TargetID = std::pair<StringRef, StringRef>;
159 
160   OffloadFile(std::unique_ptr<OffloadBinary> Binary,
161               std::unique_ptr<MemoryBuffer> Buffer)
162       : OwningBinary<OffloadBinary>(std::move(Binary), std::move(Buffer)) {}
163 
164   /// We use the Triple and Architecture pair to group linker inputs together.
165   /// This conversion function lets us use these inputs in a hash-map.
166   operator TargetID() const {
167     return std::make_pair(getBinary()->getTriple(), getBinary()->getArch());
168   }
169 };
170 
171 /// Extracts embedded device offloading code from a memory \p Buffer to a list
172 /// of \p Binaries.
173 Error extractOffloadBinaries(MemoryBufferRef Buffer,
174                              SmallVectorImpl<OffloadFile> &Binaries);
175 
176 /// Convert a string \p Name to an image kind.
177 ImageKind getImageKind(StringRef Name);
178 
179 /// Convert an image kind to its string representation.
180 StringRef getImageKindName(ImageKind Name);
181 
182 /// Convert a string \p Name to an offload kind.
183 OffloadKind getOffloadKind(StringRef Name);
184 
185 /// Convert an offload kind to its string representation.
186 StringRef getOffloadKindName(OffloadKind Name);
187 
188 } // namespace object
189 
190 } // namespace llvm
191 #endif
192