Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/lib/Object/OffloadBinary.cpp
35232 views
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
9
#include "llvm/Object/OffloadBinary.h"
10
11
#include "llvm/ADT/StringSwitch.h"
12
#include "llvm/BinaryFormat/Magic.h"
13
#include "llvm/IR/Constants.h"
14
#include "llvm/IR/Module.h"
15
#include "llvm/IRReader/IRReader.h"
16
#include "llvm/MC/StringTableBuilder.h"
17
#include "llvm/Object/Archive.h"
18
#include "llvm/Object/ArchiveWriter.h"
19
#include "llvm/Object/Binary.h"
20
#include "llvm/Object/COFF.h"
21
#include "llvm/Object/ELFObjectFile.h"
22
#include "llvm/Object/Error.h"
23
#include "llvm/Object/IRObjectFile.h"
24
#include "llvm/Object/ObjectFile.h"
25
#include "llvm/Support/Alignment.h"
26
#include "llvm/Support/FileOutputBuffer.h"
27
#include "llvm/Support/SourceMgr.h"
28
29
using namespace llvm;
30
using namespace llvm::object;
31
32
namespace {
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.
37
Error extractOffloadFiles(MemoryBufferRef Contents,
38
SmallVectorImpl<OffloadFile> &Binaries) {
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 =
43
MemoryBuffer::getMemBuffer(Contents.getBuffer().drop_front(Offset), "",
44
/*RequiresNullTerminator*/ false);
45
if (!isAddrAligned(Align(OffloadBinary::getAlignment()),
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.
70
Error extractFromObject(const ObjectFile &Obj,
71
SmallVectorImpl<OffloadFile> &Binaries) {
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
102
Error extractFromBitcode(MemoryBufferRef Buffer,
103
SmallVectorImpl<OffloadFile> &Binaries) {
104
LLVMContext Context;
105
SMDiagnostic Err;
106
std::unique_ptr<Module> M = getLazyIRModule(
107
MemoryBuffer::getMemBuffer(Buffer, /*RequiresNullTerminator=*/false), Err,
108
Context);
109
if (!M)
110
return createStringError(inconvertibleErrorCode(),
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
144
Error extractFromArchive(const Archive &Library,
145
SmallVectorImpl<OffloadFile> &Binaries) {
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.
156
if (!isAddrAligned(Align(OffloadBinary::getAlignment()),
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
173
Expected<std::unique_ptr<OffloadBinary>>
174
OffloadBinary::create(MemoryBufferRef Buf) {
175
if (Buf.getBufferSize() < sizeof(Header) + sizeof(Entry))
176
return errorCodeToError(object_error::parse_failed);
177
178
// Check for 0x10FF1OAD magic bytes.
179
if (identify_magic(Buf.getBuffer()) != file_magic::offload_binary)
180
return errorCodeToError(object_error::parse_failed);
181
182
// Make sure that the data has sufficient alignment.
183
if (!isAddrAligned(Align(getAlignment()), Buf.getBufferStart()))
184
return errorCodeToError(object_error::parse_failed);
185
186
const char *Start = Buf.getBufferStart();
187
const Header *TheHeader = reinterpret_cast<const Header *>(Start);
188
if (TheHeader->Version != OffloadBinary::Version)
189
return errorCodeToError(object_error::parse_failed);
190
191
if (TheHeader->Size > Buf.getBufferSize() ||
192
TheHeader->Size < sizeof(Entry) || TheHeader->Size < sizeof(Header))
193
return errorCodeToError(object_error::unexpected_eof);
194
195
if (TheHeader->EntryOffset > TheHeader->Size - sizeof(Entry) ||
196
TheHeader->EntrySize > TheHeader->Size - sizeof(Header))
197
return errorCodeToError(object_error::unexpected_eof);
198
199
const Entry *TheEntry =
200
reinterpret_cast<const Entry *>(&Start[TheHeader->EntryOffset]);
201
202
if (TheEntry->ImageOffset > Buf.getBufferSize() ||
203
TheEntry->StringOffset > Buf.getBufferSize())
204
return errorCodeToError(object_error::unexpected_eof);
205
206
return std::unique_ptr<OffloadBinary>(
207
new OffloadBinary(Buf, TheHeader, TheEntry));
208
}
209
210
SmallString<0> OffloadBinary::write(const OffloadingImage &OffloadingData) {
211
// Create a null-terminated string table with all the used strings.
212
StringTableBuilder StrTab(StringTableBuilder::ELF);
213
for (auto &KeyAndValue : OffloadingData.StringData) {
214
StrTab.add(KeyAndValue.first);
215
StrTab.add(KeyAndValue.second);
216
}
217
StrTab.finalize();
218
219
uint64_t StringEntrySize =
220
sizeof(StringEntry) * OffloadingData.StringData.size();
221
222
// Make sure the image we're wrapping around is aligned as well.
223
uint64_t BinaryDataSize = alignTo(sizeof(Header) + sizeof(Entry) +
224
StringEntrySize + StrTab.getSize(),
225
getAlignment());
226
227
// Create the header and fill in the offsets. The entry will be directly
228
// placed after the header in memory. Align the size to the alignment of the
229
// header so this can be placed contiguously in a single section.
230
Header TheHeader;
231
TheHeader.Size = alignTo(
232
BinaryDataSize + OffloadingData.Image->getBufferSize(), getAlignment());
233
TheHeader.EntryOffset = sizeof(Header);
234
TheHeader.EntrySize = sizeof(Entry);
235
236
// Create the entry using the string table offsets. The string table will be
237
// placed directly after the entry in memory, and the image after that.
238
Entry TheEntry;
239
TheEntry.TheImageKind = OffloadingData.TheImageKind;
240
TheEntry.TheOffloadKind = OffloadingData.TheOffloadKind;
241
TheEntry.Flags = OffloadingData.Flags;
242
TheEntry.StringOffset = sizeof(Header) + sizeof(Entry);
243
TheEntry.NumStrings = OffloadingData.StringData.size();
244
245
TheEntry.ImageOffset = BinaryDataSize;
246
TheEntry.ImageSize = OffloadingData.Image->getBufferSize();
247
248
SmallString<0> Data;
249
Data.reserve(TheHeader.Size);
250
raw_svector_ostream OS(Data);
251
OS << StringRef(reinterpret_cast<char *>(&TheHeader), sizeof(Header));
252
OS << StringRef(reinterpret_cast<char *>(&TheEntry), sizeof(Entry));
253
for (auto &KeyAndValue : OffloadingData.StringData) {
254
uint64_t Offset = sizeof(Header) + sizeof(Entry) + StringEntrySize;
255
StringEntry Map{Offset + StrTab.getOffset(KeyAndValue.first),
256
Offset + StrTab.getOffset(KeyAndValue.second)};
257
OS << StringRef(reinterpret_cast<char *>(&Map), sizeof(StringEntry));
258
}
259
StrTab.write(OS);
260
// Add padding to required image alignment.
261
OS.write_zeros(TheEntry.ImageOffset - OS.tell());
262
OS << OffloadingData.Image->getBuffer();
263
264
// Add final padding to required alignment.
265
assert(TheHeader.Size >= OS.tell() && "Too much data written?");
266
OS.write_zeros(TheHeader.Size - OS.tell());
267
assert(TheHeader.Size == OS.tell() && "Size mismatch");
268
269
return Data;
270
}
271
272
Error object::extractOffloadBinaries(MemoryBufferRef Buffer,
273
SmallVectorImpl<OffloadFile> &Binaries) {
274
file_magic Type = identify_magic(Buffer.getBuffer());
275
switch (Type) {
276
case file_magic::bitcode:
277
return extractFromBitcode(Buffer, Binaries);
278
case file_magic::elf_relocatable:
279
case file_magic::elf_executable:
280
case file_magic::elf_shared_object:
281
case file_magic::coff_object: {
282
Expected<std::unique_ptr<ObjectFile>> ObjFile =
283
ObjectFile::createObjectFile(Buffer, Type);
284
if (!ObjFile)
285
return ObjFile.takeError();
286
return extractFromObject(*ObjFile->get(), Binaries);
287
}
288
case file_magic::archive: {
289
Expected<std::unique_ptr<llvm::object::Archive>> LibFile =
290
object::Archive::create(Buffer);
291
if (!LibFile)
292
return LibFile.takeError();
293
return extractFromArchive(*LibFile->get(), Binaries);
294
}
295
case file_magic::offload_binary:
296
return extractOffloadFiles(Buffer, Binaries);
297
default:
298
return Error::success();
299
}
300
}
301
302
OffloadKind object::getOffloadKind(StringRef Name) {
303
return llvm::StringSwitch<OffloadKind>(Name)
304
.Case("openmp", OFK_OpenMP)
305
.Case("cuda", OFK_Cuda)
306
.Case("hip", OFK_HIP)
307
.Default(OFK_None);
308
}
309
310
StringRef object::getOffloadKindName(OffloadKind Kind) {
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
default:
319
return "none";
320
}
321
}
322
323
ImageKind object::getImageKind(StringRef Name) {
324
return llvm::StringSwitch<ImageKind>(Name)
325
.Case("o", IMG_Object)
326
.Case("bc", IMG_Bitcode)
327
.Case("cubin", IMG_Cubin)
328
.Case("fatbin", IMG_Fatbinary)
329
.Case("s", IMG_PTX)
330
.Default(IMG_None);
331
}
332
333
StringRef object::getImageKindName(ImageKind Kind) {
334
switch (Kind) {
335
case IMG_Object:
336
return "o";
337
case IMG_Bitcode:
338
return "bc";
339
case IMG_Cubin:
340
return "cubin";
341
case IMG_Fatbinary:
342
return "fatbin";
343
case IMG_PTX:
344
return "s";
345
default:
346
return "";
347
}
348
}
349
350
bool object::areTargetsCompatible(const OffloadFile::TargetID &LHS,
351
const OffloadFile::TargetID &RHS) {
352
// Exact matches are not considered compatible because they are the same
353
// target. We are interested in different targets that are compatible.
354
if (LHS == RHS)
355
return false;
356
357
// The triples must match at all times.
358
if (LHS.first != RHS.first)
359
return false;
360
361
// If the architecture is "all" we assume it is always compatible.
362
if (LHS.second == "generic" || RHS.second == "generic")
363
return true;
364
365
// Only The AMDGPU target requires additional checks.
366
llvm::Triple T(LHS.first);
367
if (!T.isAMDGPU())
368
return false;
369
370
// The base processor must always match.
371
if (LHS.second.split(":").first != RHS.second.split(":").first)
372
return false;
373
374
// Check combintions of on / off features that must match.
375
if (LHS.second.contains("xnack+") && RHS.second.contains("xnack-"))
376
return false;
377
if (LHS.second.contains("xnack-") && RHS.second.contains("xnack+"))
378
return false;
379
if (LHS.second.contains("sramecc-") && RHS.second.contains("sramecc+"))
380
return false;
381
if (LHS.second.contains("sramecc+") && RHS.second.contains("sramecc-"))
382
return false;
383
return true;
384
}
385
386