LLVM 19.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"
19#include "llvm/Object/Binary.h"
20#include "llvm/Object/COFF.h"
22#include "llvm/Object/Error.h"
28
29using namespace llvm;
30using namespace llvm::object;
31
32namespace {
33
34/// Attempts to extract all the embedded device images contained inside the
35/// buffer \p Contents. The buffer is expected to contain a valid offloading
36/// binary format.
37Error extractOffloadFiles(MemoryBufferRef Contents,
39 uint64_t Offset = 0;
40 // There could be multiple offloading binaries stored at this section.
41 while (Offset < Contents.getBuffer().size()) {
42 std::unique_ptr<MemoryBuffer> Buffer =
44 /*RequiresNullTerminator*/ false);
46 Buffer->getBufferStart()))
47 Buffer = MemoryBuffer::getMemBufferCopy(Buffer->getBuffer(),
48 Buffer->getBufferIdentifier());
49 auto BinaryOrErr = OffloadBinary::create(*Buffer);
50 if (!BinaryOrErr)
51 return BinaryOrErr.takeError();
52 OffloadBinary &Binary = **BinaryOrErr;
53
54 // Create a new owned binary with a copy of the original memory.
55 std::unique_ptr<MemoryBuffer> BufferCopy = MemoryBuffer::getMemBufferCopy(
56 Binary.getData().take_front(Binary.getSize()),
57 Contents.getBufferIdentifier());
58 auto NewBinaryOrErr = OffloadBinary::create(*BufferCopy);
59 if (!NewBinaryOrErr)
60 return NewBinaryOrErr.takeError();
61 Binaries.emplace_back(std::move(*NewBinaryOrErr), std::move(BufferCopy));
62
63 Offset += Binary.getSize();
64 }
65
66 return Error::success();
67}
68
69// Extract offloading binaries from an Object file \p Obj.
70Error extractFromObject(const ObjectFile &Obj,
72 assert((Obj.isELF() || Obj.isCOFF()) && "Invalid file type");
73
74 for (SectionRef Sec : Obj.sections()) {
75 // ELF files contain a section with the LLVM_OFFLOADING type.
76 if (Obj.isELF() &&
77 static_cast<ELFSectionRef>(Sec).getType() != ELF::SHT_LLVM_OFFLOADING)
78 continue;
79
80 // COFF has no section types so we rely on the name of the section.
81 if (Obj.isCOFF()) {
82 Expected<StringRef> NameOrErr = Sec.getName();
83 if (!NameOrErr)
84 return NameOrErr.takeError();
85
86 if (!NameOrErr->starts_with(".llvm.offloading"))
87 continue;
88 }
89
90 Expected<StringRef> Buffer = Sec.getContents();
91 if (!Buffer)
92 return Buffer.takeError();
93
94 MemoryBufferRef Contents(*Buffer, Obj.getFileName());
95 if (Error Err = extractOffloadFiles(Contents, Binaries))
96 return Err;
97 }
98
99 return Error::success();
100}
101
102Error extractFromBitcode(MemoryBufferRef Buffer,
105 SMDiagnostic Err;
106 std::unique_ptr<Module> M = getLazyIRModule(
107 MemoryBuffer::getMemBuffer(Buffer, /*RequiresNullTerminator=*/false), Err,
108 Context);
109 if (!M)
111 "Failed to create module");
112
113 // Extract offloading data from globals referenced by the
114 // `llvm.embedded.object` metadata with the `.llvm.offloading` section.
115 auto *MD = M->getNamedMetadata("llvm.embedded.objects");
116 if (!MD)
117 return Error::success();
118
119 for (const MDNode *Op : MD->operands()) {
120 if (Op->getNumOperands() < 2)
121 continue;
122
123 MDString *SectionID = dyn_cast<MDString>(Op->getOperand(1));
124 if (!SectionID || SectionID->getString() != ".llvm.offloading")
125 continue;
126
127 GlobalVariable *GV =
128 mdconst::dyn_extract_or_null<GlobalVariable>(Op->getOperand(0));
129 if (!GV)
130 continue;
131
132 auto *CDS = dyn_cast<ConstantDataSequential>(GV->getInitializer());
133 if (!CDS)
134 continue;
135
136 MemoryBufferRef Contents(CDS->getAsString(), M->getName());
137 if (Error Err = extractOffloadFiles(Contents, Binaries))
138 return Err;
139 }
140
141 return Error::success();
142}
143
144Error extractFromArchive(const Archive &Library,
146 // Try to extract device code from each file stored in the static archive.
147 Error Err = Error::success();
148 for (auto Child : Library.children(Err)) {
149 auto ChildBufferOrErr = Child.getMemoryBufferRef();
150 if (!ChildBufferOrErr)
151 return ChildBufferOrErr.takeError();
152 std::unique_ptr<MemoryBuffer> ChildBuffer =
153 MemoryBuffer::getMemBuffer(*ChildBufferOrErr, false);
154
155 // Check if the buffer has the required alignment.
157 ChildBuffer->getBufferStart()))
158 ChildBuffer = MemoryBuffer::getMemBufferCopy(
159 ChildBufferOrErr->getBuffer(),
160 ChildBufferOrErr->getBufferIdentifier());
161
162 if (Error Err = extractOffloadBinaries(*ChildBuffer, Binaries))
163 return Err;
164 }
165
166 if (Err)
167 return Err;
168 return Error::success();
169}
170
171} // namespace
172
175 if (Buf.getBufferSize() < sizeof(Header) + sizeof(Entry))
177
178 // Check for 0x10FF1OAD magic bytes.
181
182 // Make sure that the data has sufficient alignment.
185
186 const char *Start = Buf.getBufferStart();
187 const Header *TheHeader = reinterpret_cast<const Header *>(Start);
188 if (TheHeader->Version != OffloadBinary::Version)
190
191 if (TheHeader->Size > Buf.getBufferSize() ||
192 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())
202
203 return std::unique_ptr<OffloadBinary>(
204 new OffloadBinary(Buf, TheHeader, TheEntry));
205}
206
208 // Create a null-terminated string table with all the used strings.
210 for (auto &KeyAndValue : OffloadingData.StringData) {
211 StrTab.add(KeyAndValue.first);
212 StrTab.add(KeyAndValue.second);
213 }
214 StrTab.finalize();
215
216 uint64_t StringEntrySize =
217 sizeof(StringEntry) * OffloadingData.StringData.size();
218
219 // Make sure the image we're wrapping around is aligned as well.
220 uint64_t BinaryDataSize = alignTo(sizeof(Header) + sizeof(Entry) +
221 StringEntrySize + StrTab.getSize(),
222 getAlignment());
223
224 // Create the header and fill in the offsets. The entry will be directly
225 // placed after the header in memory. Align the size to the alignment of the
226 // header so this can be placed contiguously in a single section.
227 Header TheHeader;
228 TheHeader.Size = alignTo(
229 BinaryDataSize + OffloadingData.Image->getBufferSize(), getAlignment());
230 TheHeader.EntryOffset = sizeof(Header);
231 TheHeader.EntrySize = sizeof(Entry);
232
233 // Create the entry using the string table offsets. The string table will be
234 // placed directly after the entry in memory, and the image after that.
235 Entry TheEntry;
236 TheEntry.TheImageKind = OffloadingData.TheImageKind;
237 TheEntry.TheOffloadKind = OffloadingData.TheOffloadKind;
238 TheEntry.Flags = OffloadingData.Flags;
239 TheEntry.StringOffset = sizeof(Header) + sizeof(Entry);
240 TheEntry.NumStrings = OffloadingData.StringData.size();
241
242 TheEntry.ImageOffset = BinaryDataSize;
243 TheEntry.ImageSize = OffloadingData.Image->getBufferSize();
244
246 Data.reserve(TheHeader.Size);
248 OS << StringRef(reinterpret_cast<char *>(&TheHeader), sizeof(Header));
249 OS << StringRef(reinterpret_cast<char *>(&TheEntry), sizeof(Entry));
250 for (auto &KeyAndValue : OffloadingData.StringData) {
251 uint64_t Offset = sizeof(Header) + sizeof(Entry) + StringEntrySize;
252 StringEntry Map{Offset + StrTab.getOffset(KeyAndValue.first),
253 Offset + StrTab.getOffset(KeyAndValue.second)};
254 OS << StringRef(reinterpret_cast<char *>(&Map), sizeof(StringEntry));
255 }
256 StrTab.write(OS);
257 // Add padding to required image alignment.
258 OS.write_zeros(TheEntry.ImageOffset - OS.tell());
259 OS << OffloadingData.Image->getBuffer();
260
261 // Add final padding to required alignment.
262 assert(TheHeader.Size >= OS.tell() && "Too much data written?");
263 OS.write_zeros(TheHeader.Size - OS.tell());
264 assert(TheHeader.Size == OS.tell() && "Size mismatch");
265
266 return Data;
267}
268
272 switch (Type) {
274 return extractFromBitcode(Buffer, Binaries);
281 if (!ObjFile)
282 return ObjFile.takeError();
283 return extractFromObject(*ObjFile->get(), Binaries);
284 }
285 case file_magic::archive: {
288 if (!LibFile)
289 return LibFile.takeError();
290 return extractFromArchive(*LibFile->get(), Binaries);
291 }
293 return extractOffloadFiles(Buffer, Binaries);
294 default:
295 return Error::success();
296 }
297}
298
301 .Case("openmp", OFK_OpenMP)
302 .Case("cuda", OFK_Cuda)
303 .Case("hip", OFK_HIP)
305}
306
308 switch (Kind) {
309 case OFK_OpenMP:
310 return "openmp";
311 case OFK_Cuda:
312 return "cuda";
313 case OFK_HIP:
314 return "hip";
315 default:
316 return "none";
317 }
318}
319
322 .Case("o", IMG_Object)
323 .Case("bc", IMG_Bitcode)
324 .Case("cubin", IMG_Cubin)
325 .Case("fatbin", IMG_Fatbinary)
326 .Case("s", IMG_PTX)
328}
329
331 switch (Kind) {
332 case IMG_Object:
333 return "o";
334 case IMG_Bitcode:
335 return "bc";
336 case IMG_Cubin:
337 return "cubin";
338 case IMG_Fatbinary:
339 return "fatbin";
340 case IMG_PTX:
341 return "s";
342 default:
343 return "";
344 }
345}
346
348 const OffloadFile::TargetID &RHS) {
349 // Exact matches are not considered compatible because they are the same
350 // target. We are interested in different targets that are compatible.
351 if (LHS == RHS)
352 return false;
353
354 // The triples must match at all times.
355 if (LHS.first != RHS.first)
356 return false;
357
358 // If the architecture is "all" we assume it is always compatible.
359 if (LHS.second.equals("generic") || RHS.second.equals("generic"))
360 return true;
361
362 // Only The AMDGPU target requires additional checks.
363 llvm::Triple T(LHS.first);
364 if (!T.isAMDGPU())
365 return false;
366
367 // The base processor must always match.
368 if (LHS.second.split(":").first != RHS.second.split(":").first)
369 return false;
370
371 // Check combintions of on / off features that must match.
372 if (LHS.second.contains("xnack+") && RHS.second.contains("xnack-"))
373 return false;
374 if (LHS.second.contains("xnack-") && RHS.second.contains("xnack+"))
375 return false;
376 if (LHS.second.contains("sramecc-") && RHS.second.contains("sramecc+"))
377 return false;
378 if (LHS.second.contains("sramecc+") && RHS.second.contains("sramecc-"))
379 return false;
380 return true;
381}
This file contains the declarations for the subclasses of Constant, which represent the different fla...
std::string Name
Module.h This file contains the declarations for the Module class.
LLVMContext & Context
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
raw_pwrite_stream & OS
This file implements the StringSwitch template, which mimics a switch() statement whose cases are str...
Value * RHS
Value * LHS
This class represents an Operation in the Expression.
Lightweight error class with error context and mandatory checking.
Definition: Error.h:160
static ErrorSuccess success()
Create a success value.
Definition: Error.h:334
Tagged union holding either a T or a Error.
Definition: Error.h:474
Error takeError()
Take ownership of the stored error.
Definition: Error.h:601
reference get()
Returns a reference to the stored T value.
Definition: Error.h:571
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:67
Metadata node.
Definition: Metadata.h:1067
A single uniqued string.
Definition: Metadata.h:720
StringRef getString() const
Definition: Metadata.cpp:607
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:281
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...
Definition: SmallVector.h:586
reference emplace_back(ArgTypes &&... Args)
Definition: SmallVector.h:950
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
StringRef drop_front(size_t N=1) const
Return a StringRef equal to 'this' but with the first N elements dropped.
Definition: StringRef.h:605
constexpr size_t size() const
size - Get the string size.
Definition: StringRef.h:137
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:576
A switch()-like statement whose cases are string literals.
Definition: StringSwitch.h:44
StringSwitch & Case(StringLiteral S, T Value)
Definition: StringSwitch.h:69
R Default(T Value)
Definition: StringSwitch.h:182
Utility for building string tables with deduplicated suffixes.
size_t getOffset(CachedHashStringRef S) const
Get the offest of a string in the string table.
void write(raw_ostream &OS) const
size_t add(CachedHashStringRef S)
Add a string to the builder.
void finalize()
Analyze the strings and build the final table.
Triple - Helper class for working with autoconf configuration names.
Definition: Triple.h:44
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
static Expected< std::unique_ptr< Archive > > create(MemoryBufferRef Source)
Definition: Archive.cpp:669
MemoryBufferRef Data
Definition: Binary.h:37
StringRef getData() const
Definition: Binary.cpp:39
bool isCOFF() const
Definition: Binary.h:131
StringRef getFileName() const
Definition: Binary.cpp:41
bool isELF() const
Definition: Binary.h:123
This class is the base class for all object file types.
Definition: ObjectFile.h:229
section_iterator_range sections() const
Definition: ObjectFile.h:328
static Expected< OwningBinary< ObjectFile > > createObjectFile(StringRef ObjectPath)
Definition: ObjectFile.cpp:209
A simple binary serialization of an offloading file.
Definition: OffloadBinary.h:61
static uint64_t getAlignment()
Definition: OffloadBinary.h:84
static SmallString< 0 > write(const OffloadingImage &)
Serialize the contents of File to a binary buffer to be read later.
static 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.
Definition: OffloadBinary.h:67
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:81
raw_ostream & write_zeros(unsigned NumZeros)
write_zeros - Insert 'NumZeros' nulls.
uint64_t tell() const
tell - Return the current offset with the file.
Definition: raw_ostream.h:150
A raw_ostream that writes to an SmallVector or SmallString.
Definition: raw_ostream.h:690
@ SHT_LLVM_OFFLOADING
Definition: ELF.h:1101
Error extractOffloadBinaries(MemoryBufferRef Buffer, SmallVectorImpl< OffloadFile > &Binaries)
Extracts embedded device offloading code from a memory Buffer to a list of Binaries.
ImageKind getImageKind(StringRef Name)
Convert a string Name to an image kind.
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.
Definition: OffloadBinary.h:33
OffloadKind getOffloadKind(StringRef Name)
Convert a string Name to an offload kind.
StringRef getImageKindName(ImageKind Name)
Convert an image kind to its string representation.
ImageKind
The type of contents the offloading image contains.
Definition: OffloadBinary.h:42
StringRef getOffloadKindName(OffloadKind Name)
Convert an offload kind to its string representation.
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
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:456
std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition: Error.cpp:90
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
Definition: Error.h:1244
uint64_t alignTo(uint64_t Size, Align A)
Returns a multiple of A needed to store Size bytes.
Definition: Alignment.h:155
Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
Definition: Error.cpp:103
bool isAddrAligned(Align Lhs, const void *Addr)
Checks that Addr is a multiple of the alignment.
Definition: Alignment.h:150
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:34
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:20
@ elf_relocatable
ELF Relocatable object file.
Definition: Magic.h:27
@ archive
ar style archive file
Definition: Magic.h:25
@ elf_shared_object
ELF dynamically linked shared lib.
Definition: Magic.h:29
@ elf_executable
ELF Executable image.
Definition: Magic.h:28
@ offload_binary
LLVM offload object file.
Definition: Magic.h:57
@ bitcode
Bitcode file.
Definition: Magic.h:23
@ coff_object
COFF object file.
Definition: Magic.h:47
The offloading metadata that will be serialized to a memory buffer.
Definition: OffloadBinary.h:70
std::unique_ptr< MemoryBuffer > Image
Definition: OffloadBinary.h:75
MapVector< StringRef, StringRef > StringData
Definition: OffloadBinary.h:74