LLVM 23.0.0git
OffloadBinary.cpp
Go to the documentation of this file.
1//===- Offloading.cpp - 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
10
13#include "llvm/IR/Constants.h"
14#include "llvm/IR/Module.h"
17#include "llvm/Object/Archive.h"
18#include "llvm/Object/Binary.h"
20#include "llvm/Object/Error.h"
25
26using namespace llvm;
27using namespace llvm::object;
28
29namespace {
30
31/// Attempts to extract all the embedded device images contained inside the
32/// buffer \p Contents. The buffer is expected to contain a valid offloading
33/// binary format.
34Error extractOffloadFiles(MemoryBufferRef Contents,
36 uint64_t Offset = 0;
37 // There could be multiple offloading binaries stored at this section.
38 while (Offset < Contents.getBuffer().size()) {
39 std::unique_ptr<MemoryBuffer> Buffer =
41 /*RequiresNullTerminator*/ false);
43 Buffer->getBufferStart()))
44 Buffer = MemoryBuffer::getMemBufferCopy(Buffer->getBuffer(),
45 Buffer->getBufferIdentifier());
46 auto BinaryOrErr = OffloadBinary::create(*Buffer);
47 if (!BinaryOrErr)
48 return BinaryOrErr.takeError();
49 OffloadBinary &Binary = **BinaryOrErr;
50
51 // Create a new owned binary with a copy of the original memory.
52 std::unique_ptr<MemoryBuffer> BufferCopy = MemoryBuffer::getMemBufferCopy(
53 Binary.getData().take_front(Binary.getSize()),
54 Contents.getBufferIdentifier());
55 auto NewBinaryOrErr = OffloadBinary::create(*BufferCopy);
56 if (!NewBinaryOrErr)
57 return NewBinaryOrErr.takeError();
58 Binaries.emplace_back(std::move(*NewBinaryOrErr), std::move(BufferCopy));
59
60 Offset += Binary.getSize();
61 }
62
63 return Error::success();
64}
65
66// Extract offloading binaries from an Object file \p Obj.
67Error extractFromObject(const ObjectFile &Obj,
69 assert((Obj.isELF() || Obj.isCOFF()) && "Invalid file type");
70
71 for (SectionRef Sec : Obj.sections()) {
72 // ELF files contain a section with the LLVM_OFFLOADING type.
73 if (Obj.isELF() &&
74 static_cast<ELFSectionRef>(Sec).getType() != ELF::SHT_LLVM_OFFLOADING)
75 continue;
76
77 // COFF has no section types so we rely on the name of the section.
78 if (Obj.isCOFF()) {
79 Expected<StringRef> NameOrErr = Sec.getName();
80 if (!NameOrErr)
81 return NameOrErr.takeError();
82
83 if (!NameOrErr->starts_with(".llvm.offloading"))
84 continue;
85 }
86
87 Expected<StringRef> Buffer = Sec.getContents();
88 if (!Buffer)
89 return Buffer.takeError();
90
91 MemoryBufferRef Contents(*Buffer, Obj.getFileName());
92 if (Error Err = extractOffloadFiles(Contents, Binaries))
93 return Err;
94 }
95
96 return Error::success();
97}
98
99Error extractFromBitcode(MemoryBufferRef Buffer,
101 LLVMContext Context;
102 SMDiagnostic Err;
103 std::unique_ptr<Module> M = getLazyIRModule(
104 MemoryBuffer::getMemBuffer(Buffer, /*RequiresNullTerminator=*/false), Err,
105 Context);
106 if (!M)
108 "Failed to create module");
109
110 // Extract offloading data from globals referenced by the
111 // `llvm.embedded.object` metadata with the `.llvm.offloading` section.
112 auto *MD = M->getNamedMetadata("llvm.embedded.objects");
113 if (!MD)
114 return Error::success();
115
116 for (const MDNode *Op : MD->operands()) {
117 if (Op->getNumOperands() < 2)
118 continue;
119
120 MDString *SectionID = dyn_cast<MDString>(Op->getOperand(1));
121 if (!SectionID || SectionID->getString() != ".llvm.offloading")
122 continue;
123
124 GlobalVariable *GV =
126 if (!GV)
127 continue;
128
130 if (!CDS)
131 continue;
132
133 MemoryBufferRef Contents(CDS->getAsString(), M->getName());
134 if (Error Err = extractOffloadFiles(Contents, Binaries))
135 return Err;
136 }
137
138 return Error::success();
139}
140
141Error extractFromArchive(const Archive &Library,
143 // Try to extract device code from each file stored in the static archive.
144 Error Err = Error::success();
145 for (auto Child : Library.children(Err)) {
146 auto ChildBufferOrErr = Child.getMemoryBufferRef();
147 if (!ChildBufferOrErr)
148 return ChildBufferOrErr.takeError();
149 std::unique_ptr<MemoryBuffer> ChildBuffer =
150 MemoryBuffer::getMemBuffer(*ChildBufferOrErr, false);
151
152 // Check if the buffer has the required alignment.
154 ChildBuffer->getBufferStart()))
155 ChildBuffer = MemoryBuffer::getMemBufferCopy(
156 ChildBufferOrErr->getBuffer(),
157 ChildBufferOrErr->getBufferIdentifier());
158
159 if (Error Err = extractOffloadBinaries(*ChildBuffer, Binaries))
160 return Err;
161 }
162
163 if (Err)
164 return Err;
165 return Error::success();
166}
167
168} // namespace
169
172 if (Buf.getBufferSize() < sizeof(Header) + sizeof(Entry))
174
175 // Check for 0x10FF1OAD magic bytes.
178
179 // Make sure that the data has sufficient alignment.
182
183 const char *Start = Buf.getBufferStart();
184 const Header *TheHeader = reinterpret_cast<const Header *>(Start);
185 if (TheHeader->Version != OffloadBinary::Version)
187
188 if (TheHeader->Size > Buf.getBufferSize() ||
189 TheHeader->Size < sizeof(Entry) || TheHeader->Size < sizeof(Header))
191
192 if (TheHeader->EntryOffset > TheHeader->Size - sizeof(Entry) ||
193 TheHeader->EntrySize > TheHeader->Size - sizeof(Header))
195
196 const Entry *TheEntry =
197 reinterpret_cast<const Entry *>(&Start[TheHeader->EntryOffset]);
198
199 if (TheEntry->ImageOffset > Buf.getBufferSize() ||
200 TheEntry->StringOffset > Buf.getBufferSize() ||
201 TheEntry->StringOffset + TheEntry->NumStrings * sizeof(StringEntry) >
202 Buf.getBufferSize())
204
205 return std::unique_ptr<OffloadBinary>(
206 new OffloadBinary(Buf, TheHeader, TheEntry));
207}
208
210 // Create a null-terminated string table with all the used strings.
212 for (auto &KeyAndValue : OffloadingData.StringData) {
213 StrTab.add(KeyAndValue.first);
214 StrTab.add(KeyAndValue.second);
215 }
216 StrTab.finalize();
217
218 uint64_t StringEntrySize =
219 sizeof(StringEntry) * OffloadingData.StringData.size();
220
221 // Make sure the image we're wrapping around is aligned as well.
222 uint64_t BinaryDataSize = alignTo(sizeof(Header) + sizeof(Entry) +
223 StringEntrySize + StrTab.getSize(),
224 getAlignment());
225
226 // Create the header and fill in the offsets. The entry will be directly
227 // placed after the header in memory. Align the size to the alignment of the
228 // header so this can be placed contiguously in a single section.
229 Header TheHeader;
230 TheHeader.Size = alignTo(
231 BinaryDataSize + OffloadingData.Image->getBufferSize(), getAlignment());
232 TheHeader.EntryOffset = sizeof(Header);
233 TheHeader.EntrySize = sizeof(Entry);
234
235 // Create the entry using the string table offsets. The string table will be
236 // placed directly after the entry in memory, and the image after that.
237 Entry TheEntry;
238 TheEntry.TheImageKind = OffloadingData.TheImageKind;
239 TheEntry.TheOffloadKind = OffloadingData.TheOffloadKind;
240 TheEntry.Flags = OffloadingData.Flags;
241 TheEntry.StringOffset = sizeof(Header) + sizeof(Entry);
242 TheEntry.NumStrings = OffloadingData.StringData.size();
243
244 TheEntry.ImageOffset = BinaryDataSize;
245 TheEntry.ImageSize = OffloadingData.Image->getBufferSize();
246
248 Data.reserve(TheHeader.Size);
250 OS << StringRef(reinterpret_cast<char *>(&TheHeader), sizeof(Header));
251 OS << StringRef(reinterpret_cast<char *>(&TheEntry), sizeof(Entry));
252 for (auto &KeyAndValue : OffloadingData.StringData) {
253 uint64_t Offset = sizeof(Header) + sizeof(Entry) + StringEntrySize;
254 StringEntry Map{Offset + StrTab.getOffset(KeyAndValue.first),
255 Offset + StrTab.getOffset(KeyAndValue.second)};
256 OS << StringRef(reinterpret_cast<char *>(&Map), sizeof(StringEntry));
257 }
258 StrTab.write(OS);
259 // Add padding to required image alignment.
260 OS.write_zeros(TheEntry.ImageOffset - OS.tell());
261 OS << OffloadingData.Image->getBuffer();
262
263 // Add final padding to required alignment.
264 assert(TheHeader.Size >= OS.tell() && "Too much data written?");
265 OS.write_zeros(TheHeader.Size - OS.tell());
266 assert(TheHeader.Size == OS.tell() && "Size mismatch");
267
268 return Data;
269}
270
274 switch (Type) {
276 return extractFromBitcode(Buffer, Binaries);
283 if (!ObjFile)
284 return ObjFile.takeError();
285 return extractFromObject(*ObjFile->get(), Binaries);
286 }
287 case file_magic::archive: {
290 if (!LibFile)
291 return LibFile.takeError();
292 return extractFromArchive(*LibFile->get(), Binaries);
293 }
295 return extractOffloadFiles(Buffer, Binaries);
296 default:
297 return Error::success();
298 }
299}
300
303 .Case("openmp", OFK_OpenMP)
304 .Case("cuda", OFK_Cuda)
305 .Case("hip", OFK_HIP)
306 .Case("sycl", OFK_SYCL)
308}
309
311 switch (Kind) {
312 case OFK_OpenMP:
313 return "openmp";
314 case OFK_Cuda:
315 return "cuda";
316 case OFK_HIP:
317 return "hip";
318 case OFK_SYCL:
319 return "sycl";
320 default:
321 return "none";
322 }
323}
324
327 .Case("o", IMG_Object)
328 .Case("bc", IMG_Bitcode)
329 .Case("cubin", IMG_Cubin)
330 .Case("fatbin", IMG_Fatbinary)
331 .Case("s", IMG_PTX)
333}
334
336 switch (Kind) {
337 case IMG_Object:
338 return "o";
339 case IMG_Bitcode:
340 return "bc";
341 case IMG_Cubin:
342 return "cubin";
343 case IMG_Fatbinary:
344 return "fatbin";
345 case IMG_PTX:
346 return "s";
347 default:
348 return "";
349 }
350}
351
353 const OffloadFile::TargetID &RHS) {
354 // Exact matches are not considered compatible because they are the same
355 // target. We are interested in different targets that are compatible.
356 if (LHS == RHS)
357 return false;
358
359 // The triples must match at all times.
360 if (LHS.first != RHS.first)
361 return false;
362
363 // If the architecture is "all" we assume it is always compatible.
364 if (LHS.second == "generic" || RHS.second == "generic")
365 return true;
366
367 // Only The AMDGPU target requires additional checks.
368 llvm::Triple T(LHS.first);
369 if (!T.isAMDGPU())
370 return false;
371
372 // The base processor must always match.
373 if (LHS.second.split(":").first != RHS.second.split(":").first)
374 return false;
375
376 // Check combintions of on / off features that must match.
377 if (LHS.second.contains("xnack+") && RHS.second.contains("xnack-"))
378 return false;
379 if (LHS.second.contains("xnack-") && RHS.second.contains("xnack+"))
380 return false;
381 if (LHS.second.contains("sramecc-") && RHS.second.contains("sramecc+"))
382 return false;
383 if (LHS.second.contains("sramecc+") && RHS.second.contains("sramecc-"))
384 return false;
385 return true;
386}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
Module.h This file contains the declarations for the Module class.
#define T
This file implements the StringSwitch template, which mimics a switch() statement whose cases are str...
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Error takeError()
Take ownership of the stored error.
Definition Error.h:612
reference get()
Returns a reference to the stored T value.
Definition Error.h:582
const Constant * getInitializer() const
getInitializer - Return the initializer for this global variable.
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:68
Metadata node.
Definition Metadata.h:1080
A single uniqued string.
Definition Metadata.h:722
LLVM_ABI StringRef getString() const
Definition Metadata.cpp:632
size_t getBufferSize() const
StringRef getBufferIdentifier() const
const char * getBufferStart() const
StringRef getBuffer() const
static std::unique_ptr< MemoryBuffer > getMemBuffer(StringRef InputData, StringRef BufferName="", bool RequiresNullTerminator=true)
Open the specified memory range as a MemoryBuffer.
static std::unique_ptr< MemoryBuffer > getMemBufferCopy(StringRef InputData, const Twine &BufferName="")
Open the specified memory range as a MemoryBuffer, copying the contents and taking ownership of it.
Instances of this class encapsulate one diagnostic report, allowing printing to a raw_ostream as a ca...
Definition SourceMgr.h:297
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition SmallString.h:26
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
reference emplace_back(ArgTypes &&... Args)
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
StringRef drop_front(size_t N=1) const
Return a StringRef equal to 'this' but with the first N elements dropped.
Definition StringRef.h:611
constexpr size_t size() const
size - Get the string size.
Definition StringRef.h:146
StringRef take_front(size_t N=1) const
Return a StringRef equal to 'this' but with only the first N elements remaining.
Definition StringRef.h:582
A switch()-like statement whose cases are string literals.
StringSwitch & Case(StringLiteral S, T Value)
Utility for building string tables with deduplicated suffixes.
LLVM_ABI size_t getOffset(CachedHashStringRef S) const
Get the offest of a string in the string table.
LLVM_ABI size_t add(CachedHashStringRef S, uint8_t Priority=0)
Add a string to the builder.
LLVM_ABI void write(raw_ostream &OS) const
LLVM_ABI void finalize()
Analyze the strings and build the final table.
Triple - Helper class for working with autoconf configuration names.
Definition Triple.h:47
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:45
iterator_range< child_iterator > children(Error &Err, bool SkipInternal=true) const
Definition Archive.h:353
static Expected< std::unique_ptr< Archive > > create(MemoryBufferRef Source)
Definition Archive.cpp:664
MemoryBufferRef Data
Definition Binary.h:38
StringRef getData() const
Definition Binary.cpp:39
This class is the base class for all object file types.
Definition ObjectFile.h:231
static Expected< OwningBinary< ObjectFile > > createObjectFile(StringRef ObjectPath)
A simple binary serialization of an offloading file.
static uint64_t getAlignment()
static LLVM_ABI SmallString< 0 > write(const OffloadingImage &)
Serialize the contents of File to a binary buffer to be read later.
static LLVM_ABI Expected< std::unique_ptr< OffloadBinary > > create(MemoryBufferRef)
Attempt to parse the offloading binary stored in Data.
static const uint32_t Version
The current version of the binary used for backwards compatibility.
std::pair< StringRef, StringRef > TargetID
This is a value type class that represents a single section in the list of sections in the object fil...
Definition ObjectFile.h:83
raw_ostream & write_zeros(unsigned NumZeros)
write_zeros - Insert 'NumZeros' nulls.
uint64_t tell() const
tell - Return the current offset with the file.
A raw_ostream that writes to an SmallVector or SmallString.
@ SHT_LLVM_OFFLOADING
Definition ELF.h:1186
std::enable_if_t< detail::IsValidPointer< X, Y >::value, X * > dyn_extract_or_null(Y &&MD)
Extract a Value from Metadata, if any, allowing null.
Definition Metadata.h:709
LLVM_ABI Error extractOffloadBinaries(MemoryBufferRef Buffer, SmallVectorImpl< OffloadFile > &Binaries)
Extracts embedded device offloading code from a memory Buffer to a list of Binaries.
LLVM_ABI ImageKind getImageKind(StringRef Name)
Convert a string Name to an image kind.
LLVM_ABI bool areTargetsCompatible(const OffloadFile::TargetID &LHS, const OffloadFile::TargetID &RHS)
If the target is AMD we check the target IDs for mutual compatibility.
OffloadKind
The producer of the associated offloading image.
LLVM_ABI OffloadKind getOffloadKind(StringRef Name)
Convert a string Name to an offload kind.
LLVM_ABI StringRef getImageKindName(ImageKind Name)
Convert an image kind to its string representation.
ImageKind
The type of contents the offloading image contains.
LLVM_ABI StringRef getOffloadKindName(OffloadKind Name)
Convert an offload kind to its string representation.
This is an optimization pass for GlobalISel generic memory operations.
Definition Types.h:26
LLVM_ABI file_magic identify_magic(StringRef magic)
Identify the type of a binary file based on how magical it is.
Definition Magic.cpp:33
@ Offset
Definition DWP.cpp:532
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition Error.h:1305
uint64_t alignTo(uint64_t Size, Align A)
Returns a multiple of A needed to store Size bytes.
Definition Alignment.h:144
DWARFExpression::Operation Op
LLVM_ABI Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
Definition Error.cpp:107
bool isAddrAligned(Align Lhs, const void *Addr)
Checks that Addr is a multiple of the alignment.
Definition Alignment.h:139
LLVM_ABI std::unique_ptr< Module > getLazyIRModule(std::unique_ptr< MemoryBuffer > Buffer, SMDiagnostic &Err, LLVMContext &Context, bool ShouldLazyLoadMetadata=false)
If the given MemoryBuffer holds a bitcode image, return a Module for it which does lazy deserializati...
Definition IRReader.cpp:36
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition Alignment.h:39
file_magic - An "enum class" enumeration of file types based on magic (the first N bytes of the file)...
Definition Magic.h:21
@ elf_relocatable
ELF Relocatable object file.
Definition Magic.h:28
@ archive
ar style archive file
Definition Magic.h:26
@ elf_shared_object
ELF dynamically linked shared lib.
Definition Magic.h:30
@ elf_executable
ELF Executable image.
Definition Magic.h:29
@ offload_binary
LLVM offload object file.
Definition Magic.h:58
@ bitcode
Bitcode file.
Definition Magic.h:24
@ coff_object
COFF object file.
Definition Magic.h:48
The offloading metadata that will be serialized to a memory buffer.
std::unique_ptr< MemoryBuffer > Image
MapVector< StringRef, StringRef > StringData