xref: /src/contrib/llvm-project/llvm/lib/Object/OffloadBinary.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1145449b1SDimitry Andric //===- Offloading.cpp - Utilities for handling offloading code  -*- C++ -*-===//
2145449b1SDimitry Andric //
3145449b1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4145449b1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5145449b1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6145449b1SDimitry Andric //
7145449b1SDimitry Andric //===----------------------------------------------------------------------===//
8145449b1SDimitry Andric 
9145449b1SDimitry Andric #include "llvm/Object/OffloadBinary.h"
10145449b1SDimitry Andric 
11145449b1SDimitry Andric #include "llvm/ADT/StringSwitch.h"
12145449b1SDimitry Andric #include "llvm/BinaryFormat/Magic.h"
13e3b55780SDimitry Andric #include "llvm/IR/Constants.h"
14e3b55780SDimitry Andric #include "llvm/IR/Module.h"
15e3b55780SDimitry Andric #include "llvm/IRReader/IRReader.h"
16145449b1SDimitry Andric #include "llvm/MC/StringTableBuilder.h"
17e3b55780SDimitry Andric #include "llvm/Object/Archive.h"
18e3b55780SDimitry Andric #include "llvm/Object/ArchiveWriter.h"
19e3b55780SDimitry Andric #include "llvm/Object/Binary.h"
20e3b55780SDimitry Andric #include "llvm/Object/COFF.h"
21e3b55780SDimitry Andric #include "llvm/Object/ELFObjectFile.h"
22145449b1SDimitry Andric #include "llvm/Object/Error.h"
23e3b55780SDimitry Andric #include "llvm/Object/IRObjectFile.h"
24e3b55780SDimitry Andric #include "llvm/Object/ObjectFile.h"
25145449b1SDimitry Andric #include "llvm/Support/Alignment.h"
26145449b1SDimitry Andric #include "llvm/Support/FileOutputBuffer.h"
27e3b55780SDimitry Andric #include "llvm/Support/SourceMgr.h"
28145449b1SDimitry Andric 
29145449b1SDimitry Andric using namespace llvm;
30145449b1SDimitry Andric using namespace llvm::object;
31145449b1SDimitry Andric 
32e3b55780SDimitry Andric namespace {
33e3b55780SDimitry Andric 
34e3b55780SDimitry Andric /// Attempts to extract all the embedded device images contained inside the
35e3b55780SDimitry Andric /// buffer \p Contents. The buffer is expected to contain a valid offloading
36e3b55780SDimitry Andric /// binary format.
extractOffloadFiles(MemoryBufferRef Contents,SmallVectorImpl<OffloadFile> & Binaries)37e3b55780SDimitry Andric Error extractOffloadFiles(MemoryBufferRef Contents,
38e3b55780SDimitry Andric                           SmallVectorImpl<OffloadFile> &Binaries) {
39e3b55780SDimitry Andric   uint64_t Offset = 0;
40e3b55780SDimitry Andric   // There could be multiple offloading binaries stored at this section.
41e3b55780SDimitry Andric   while (Offset < Contents.getBuffer().size()) {
42e3b55780SDimitry Andric     std::unique_ptr<MemoryBuffer> Buffer =
43e3b55780SDimitry Andric         MemoryBuffer::getMemBuffer(Contents.getBuffer().drop_front(Offset), "",
44e3b55780SDimitry Andric                                    /*RequiresNullTerminator*/ false);
45e3b55780SDimitry Andric     if (!isAddrAligned(Align(OffloadBinary::getAlignment()),
46e3b55780SDimitry Andric                        Buffer->getBufferStart()))
47e3b55780SDimitry Andric       Buffer = MemoryBuffer::getMemBufferCopy(Buffer->getBuffer(),
48e3b55780SDimitry Andric                                               Buffer->getBufferIdentifier());
49e3b55780SDimitry Andric     auto BinaryOrErr = OffloadBinary::create(*Buffer);
50e3b55780SDimitry Andric     if (!BinaryOrErr)
51e3b55780SDimitry Andric       return BinaryOrErr.takeError();
52e3b55780SDimitry Andric     OffloadBinary &Binary = **BinaryOrErr;
53e3b55780SDimitry Andric 
54e3b55780SDimitry Andric     // Create a new owned binary with a copy of the original memory.
55e3b55780SDimitry Andric     std::unique_ptr<MemoryBuffer> BufferCopy = MemoryBuffer::getMemBufferCopy(
56e3b55780SDimitry Andric         Binary.getData().take_front(Binary.getSize()),
57e3b55780SDimitry Andric         Contents.getBufferIdentifier());
58e3b55780SDimitry Andric     auto NewBinaryOrErr = OffloadBinary::create(*BufferCopy);
59e3b55780SDimitry Andric     if (!NewBinaryOrErr)
60e3b55780SDimitry Andric       return NewBinaryOrErr.takeError();
61e3b55780SDimitry Andric     Binaries.emplace_back(std::move(*NewBinaryOrErr), std::move(BufferCopy));
62e3b55780SDimitry Andric 
63e3b55780SDimitry Andric     Offset += Binary.getSize();
64e3b55780SDimitry Andric   }
65e3b55780SDimitry Andric 
66e3b55780SDimitry Andric   return Error::success();
67e3b55780SDimitry Andric }
68e3b55780SDimitry Andric 
69e3b55780SDimitry Andric // Extract offloading binaries from an Object file \p Obj.
extractFromObject(const ObjectFile & Obj,SmallVectorImpl<OffloadFile> & Binaries)70e3b55780SDimitry Andric Error extractFromObject(const ObjectFile &Obj,
71e3b55780SDimitry Andric                         SmallVectorImpl<OffloadFile> &Binaries) {
72e3b55780SDimitry Andric   assert((Obj.isELF() || Obj.isCOFF()) && "Invalid file type");
73e3b55780SDimitry Andric 
74e3b55780SDimitry Andric   for (SectionRef Sec : Obj.sections()) {
75e3b55780SDimitry Andric     // ELF files contain a section with the LLVM_OFFLOADING type.
76e3b55780SDimitry Andric     if (Obj.isELF() &&
77e3b55780SDimitry Andric         static_cast<ELFSectionRef>(Sec).getType() != ELF::SHT_LLVM_OFFLOADING)
78e3b55780SDimitry Andric       continue;
79e3b55780SDimitry Andric 
80e3b55780SDimitry Andric     // COFF has no section types so we rely on the name of the section.
81e3b55780SDimitry Andric     if (Obj.isCOFF()) {
82e3b55780SDimitry Andric       Expected<StringRef> NameOrErr = Sec.getName();
83e3b55780SDimitry Andric       if (!NameOrErr)
84e3b55780SDimitry Andric         return NameOrErr.takeError();
85e3b55780SDimitry Andric 
86ac9a064cSDimitry Andric       if (!NameOrErr->starts_with(".llvm.offloading"))
87e3b55780SDimitry Andric         continue;
88e3b55780SDimitry Andric     }
89e3b55780SDimitry Andric 
90e3b55780SDimitry Andric     Expected<StringRef> Buffer = Sec.getContents();
91e3b55780SDimitry Andric     if (!Buffer)
92e3b55780SDimitry Andric       return Buffer.takeError();
93e3b55780SDimitry Andric 
94e3b55780SDimitry Andric     MemoryBufferRef Contents(*Buffer, Obj.getFileName());
95e3b55780SDimitry Andric     if (Error Err = extractOffloadFiles(Contents, Binaries))
96e3b55780SDimitry Andric       return Err;
97e3b55780SDimitry Andric   }
98e3b55780SDimitry Andric 
99e3b55780SDimitry Andric   return Error::success();
100e3b55780SDimitry Andric }
101e3b55780SDimitry Andric 
extractFromBitcode(MemoryBufferRef Buffer,SmallVectorImpl<OffloadFile> & Binaries)102e3b55780SDimitry Andric Error extractFromBitcode(MemoryBufferRef Buffer,
103e3b55780SDimitry Andric                          SmallVectorImpl<OffloadFile> &Binaries) {
104e3b55780SDimitry Andric   LLVMContext Context;
105e3b55780SDimitry Andric   SMDiagnostic Err;
106e3b55780SDimitry Andric   std::unique_ptr<Module> M = getLazyIRModule(
107e3b55780SDimitry Andric       MemoryBuffer::getMemBuffer(Buffer, /*RequiresNullTerminator=*/false), Err,
108e3b55780SDimitry Andric       Context);
109e3b55780SDimitry Andric   if (!M)
110e3b55780SDimitry Andric     return createStringError(inconvertibleErrorCode(),
111e3b55780SDimitry Andric                              "Failed to create module");
112e3b55780SDimitry Andric 
113e3b55780SDimitry Andric   // Extract offloading data from globals referenced by the
114e3b55780SDimitry Andric   // `llvm.embedded.object` metadata with the `.llvm.offloading` section.
115e3b55780SDimitry Andric   auto *MD = M->getNamedMetadata("llvm.embedded.objects");
116e3b55780SDimitry Andric   if (!MD)
117e3b55780SDimitry Andric     return Error::success();
118e3b55780SDimitry Andric 
119e3b55780SDimitry Andric   for (const MDNode *Op : MD->operands()) {
120e3b55780SDimitry Andric     if (Op->getNumOperands() < 2)
121e3b55780SDimitry Andric       continue;
122e3b55780SDimitry Andric 
123e3b55780SDimitry Andric     MDString *SectionID = dyn_cast<MDString>(Op->getOperand(1));
124e3b55780SDimitry Andric     if (!SectionID || SectionID->getString() != ".llvm.offloading")
125e3b55780SDimitry Andric       continue;
126e3b55780SDimitry Andric 
127e3b55780SDimitry Andric     GlobalVariable *GV =
128e3b55780SDimitry Andric         mdconst::dyn_extract_or_null<GlobalVariable>(Op->getOperand(0));
129e3b55780SDimitry Andric     if (!GV)
130e3b55780SDimitry Andric       continue;
131e3b55780SDimitry Andric 
132e3b55780SDimitry Andric     auto *CDS = dyn_cast<ConstantDataSequential>(GV->getInitializer());
133e3b55780SDimitry Andric     if (!CDS)
134e3b55780SDimitry Andric       continue;
135e3b55780SDimitry Andric 
136e3b55780SDimitry Andric     MemoryBufferRef Contents(CDS->getAsString(), M->getName());
137e3b55780SDimitry Andric     if (Error Err = extractOffloadFiles(Contents, Binaries))
138e3b55780SDimitry Andric       return Err;
139e3b55780SDimitry Andric   }
140e3b55780SDimitry Andric 
141e3b55780SDimitry Andric   return Error::success();
142e3b55780SDimitry Andric }
143e3b55780SDimitry Andric 
extractFromArchive(const Archive & Library,SmallVectorImpl<OffloadFile> & Binaries)144e3b55780SDimitry Andric Error extractFromArchive(const Archive &Library,
145e3b55780SDimitry Andric                          SmallVectorImpl<OffloadFile> &Binaries) {
146e3b55780SDimitry Andric   // Try to extract device code from each file stored in the static archive.
147e3b55780SDimitry Andric   Error Err = Error::success();
148e3b55780SDimitry Andric   for (auto Child : Library.children(Err)) {
149e3b55780SDimitry Andric     auto ChildBufferOrErr = Child.getMemoryBufferRef();
150e3b55780SDimitry Andric     if (!ChildBufferOrErr)
151e3b55780SDimitry Andric       return ChildBufferOrErr.takeError();
152e3b55780SDimitry Andric     std::unique_ptr<MemoryBuffer> ChildBuffer =
153e3b55780SDimitry Andric         MemoryBuffer::getMemBuffer(*ChildBufferOrErr, false);
154e3b55780SDimitry Andric 
155e3b55780SDimitry Andric     // Check if the buffer has the required alignment.
156e3b55780SDimitry Andric     if (!isAddrAligned(Align(OffloadBinary::getAlignment()),
157e3b55780SDimitry Andric                        ChildBuffer->getBufferStart()))
158e3b55780SDimitry Andric       ChildBuffer = MemoryBuffer::getMemBufferCopy(
159e3b55780SDimitry Andric           ChildBufferOrErr->getBuffer(),
160e3b55780SDimitry Andric           ChildBufferOrErr->getBufferIdentifier());
161e3b55780SDimitry Andric 
162e3b55780SDimitry Andric     if (Error Err = extractOffloadBinaries(*ChildBuffer, Binaries))
163e3b55780SDimitry Andric       return Err;
164e3b55780SDimitry Andric   }
165e3b55780SDimitry Andric 
166e3b55780SDimitry Andric   if (Err)
167e3b55780SDimitry Andric     return Err;
168e3b55780SDimitry Andric   return Error::success();
169e3b55780SDimitry Andric }
170e3b55780SDimitry Andric 
171e3b55780SDimitry Andric } // namespace
172e3b55780SDimitry Andric 
173145449b1SDimitry Andric Expected<std::unique_ptr<OffloadBinary>>
create(MemoryBufferRef Buf)174145449b1SDimitry Andric OffloadBinary::create(MemoryBufferRef Buf) {
175145449b1SDimitry Andric   if (Buf.getBufferSize() < sizeof(Header) + sizeof(Entry))
176145449b1SDimitry Andric     return errorCodeToError(object_error::parse_failed);
177145449b1SDimitry Andric 
178145449b1SDimitry Andric   // Check for 0x10FF1OAD magic bytes.
179145449b1SDimitry Andric   if (identify_magic(Buf.getBuffer()) != file_magic::offload_binary)
180145449b1SDimitry Andric     return errorCodeToError(object_error::parse_failed);
181145449b1SDimitry Andric 
182145449b1SDimitry Andric   // Make sure that the data has sufficient alignment.
183145449b1SDimitry Andric   if (!isAddrAligned(Align(getAlignment()), Buf.getBufferStart()))
184145449b1SDimitry Andric     return errorCodeToError(object_error::parse_failed);
185145449b1SDimitry Andric 
186145449b1SDimitry Andric   const char *Start = Buf.getBufferStart();
187145449b1SDimitry Andric   const Header *TheHeader = reinterpret_cast<const Header *>(Start);
188145449b1SDimitry Andric   if (TheHeader->Version != OffloadBinary::Version)
189145449b1SDimitry Andric     return errorCodeToError(object_error::parse_failed);
190145449b1SDimitry Andric 
191145449b1SDimitry Andric   if (TheHeader->Size > Buf.getBufferSize() ||
192ac9a064cSDimitry Andric       TheHeader->Size < sizeof(Entry) || TheHeader->Size < sizeof(Header))
193ac9a064cSDimitry Andric     return errorCodeToError(object_error::unexpected_eof);
194ac9a064cSDimitry Andric 
195ac9a064cSDimitry Andric   if (TheHeader->EntryOffset > TheHeader->Size - sizeof(Entry) ||
196145449b1SDimitry Andric       TheHeader->EntrySize > TheHeader->Size - sizeof(Header))
197145449b1SDimitry Andric     return errorCodeToError(object_error::unexpected_eof);
198145449b1SDimitry Andric 
199145449b1SDimitry Andric   const Entry *TheEntry =
200145449b1SDimitry Andric       reinterpret_cast<const Entry *>(&Start[TheHeader->EntryOffset]);
201145449b1SDimitry Andric 
202145449b1SDimitry Andric   if (TheEntry->ImageOffset > Buf.getBufferSize() ||
203145449b1SDimitry Andric       TheEntry->StringOffset > Buf.getBufferSize())
204145449b1SDimitry Andric     return errorCodeToError(object_error::unexpected_eof);
205145449b1SDimitry Andric 
206145449b1SDimitry Andric   return std::unique_ptr<OffloadBinary>(
207145449b1SDimitry Andric       new OffloadBinary(Buf, TheHeader, TheEntry));
208145449b1SDimitry Andric }
209145449b1SDimitry Andric 
write(const OffloadingImage & OffloadingData)210b1c73532SDimitry Andric SmallString<0> OffloadBinary::write(const OffloadingImage &OffloadingData) {
211145449b1SDimitry Andric   // Create a null-terminated string table with all the used strings.
212145449b1SDimitry Andric   StringTableBuilder StrTab(StringTableBuilder::ELF);
213145449b1SDimitry Andric   for (auto &KeyAndValue : OffloadingData.StringData) {
2147fa27ce4SDimitry Andric     StrTab.add(KeyAndValue.first);
2157fa27ce4SDimitry Andric     StrTab.add(KeyAndValue.second);
216145449b1SDimitry Andric   }
217145449b1SDimitry Andric   StrTab.finalize();
218145449b1SDimitry Andric 
219145449b1SDimitry Andric   uint64_t StringEntrySize =
220145449b1SDimitry Andric       sizeof(StringEntry) * OffloadingData.StringData.size();
221145449b1SDimitry Andric 
222145449b1SDimitry Andric   // Make sure the image we're wrapping around is aligned as well.
223145449b1SDimitry Andric   uint64_t BinaryDataSize = alignTo(sizeof(Header) + sizeof(Entry) +
224145449b1SDimitry Andric                                         StringEntrySize + StrTab.getSize(),
225145449b1SDimitry Andric                                     getAlignment());
226145449b1SDimitry Andric 
227145449b1SDimitry Andric   // Create the header and fill in the offsets. The entry will be directly
228145449b1SDimitry Andric   // placed after the header in memory. Align the size to the alignment of the
229145449b1SDimitry Andric   // header so this can be placed contiguously in a single section.
230145449b1SDimitry Andric   Header TheHeader;
231145449b1SDimitry Andric   TheHeader.Size = alignTo(
232145449b1SDimitry Andric       BinaryDataSize + OffloadingData.Image->getBufferSize(), getAlignment());
233145449b1SDimitry Andric   TheHeader.EntryOffset = sizeof(Header);
234145449b1SDimitry Andric   TheHeader.EntrySize = sizeof(Entry);
235145449b1SDimitry Andric 
236145449b1SDimitry Andric   // Create the entry using the string table offsets. The string table will be
237145449b1SDimitry Andric   // placed directly after the entry in memory, and the image after that.
238145449b1SDimitry Andric   Entry TheEntry;
239145449b1SDimitry Andric   TheEntry.TheImageKind = OffloadingData.TheImageKind;
240145449b1SDimitry Andric   TheEntry.TheOffloadKind = OffloadingData.TheOffloadKind;
241145449b1SDimitry Andric   TheEntry.Flags = OffloadingData.Flags;
242145449b1SDimitry Andric   TheEntry.StringOffset = sizeof(Header) + sizeof(Entry);
243145449b1SDimitry Andric   TheEntry.NumStrings = OffloadingData.StringData.size();
244145449b1SDimitry Andric 
245145449b1SDimitry Andric   TheEntry.ImageOffset = BinaryDataSize;
246145449b1SDimitry Andric   TheEntry.ImageSize = OffloadingData.Image->getBufferSize();
247145449b1SDimitry Andric 
248b1c73532SDimitry Andric   SmallString<0> Data;
249145449b1SDimitry Andric   Data.reserve(TheHeader.Size);
250145449b1SDimitry Andric   raw_svector_ostream OS(Data);
251145449b1SDimitry Andric   OS << StringRef(reinterpret_cast<char *>(&TheHeader), sizeof(Header));
252145449b1SDimitry Andric   OS << StringRef(reinterpret_cast<char *>(&TheEntry), sizeof(Entry));
253145449b1SDimitry Andric   for (auto &KeyAndValue : OffloadingData.StringData) {
254145449b1SDimitry Andric     uint64_t Offset = sizeof(Header) + sizeof(Entry) + StringEntrySize;
2557fa27ce4SDimitry Andric     StringEntry Map{Offset + StrTab.getOffset(KeyAndValue.first),
2567fa27ce4SDimitry Andric                     Offset + StrTab.getOffset(KeyAndValue.second)};
257145449b1SDimitry Andric     OS << StringRef(reinterpret_cast<char *>(&Map), sizeof(StringEntry));
258145449b1SDimitry Andric   }
259145449b1SDimitry Andric   StrTab.write(OS);
260145449b1SDimitry Andric   // Add padding to required image alignment.
261145449b1SDimitry Andric   OS.write_zeros(TheEntry.ImageOffset - OS.tell());
262145449b1SDimitry Andric   OS << OffloadingData.Image->getBuffer();
263145449b1SDimitry Andric 
264145449b1SDimitry Andric   // Add final padding to required alignment.
265145449b1SDimitry Andric   assert(TheHeader.Size >= OS.tell() && "Too much data written?");
266145449b1SDimitry Andric   OS.write_zeros(TheHeader.Size - OS.tell());
267145449b1SDimitry Andric   assert(TheHeader.Size == OS.tell() && "Size mismatch");
268145449b1SDimitry Andric 
269b1c73532SDimitry Andric   return Data;
270145449b1SDimitry Andric }
271145449b1SDimitry Andric 
extractOffloadBinaries(MemoryBufferRef Buffer,SmallVectorImpl<OffloadFile> & Binaries)272e3b55780SDimitry Andric Error object::extractOffloadBinaries(MemoryBufferRef Buffer,
273e3b55780SDimitry Andric                                      SmallVectorImpl<OffloadFile> &Binaries) {
274e3b55780SDimitry Andric   file_magic Type = identify_magic(Buffer.getBuffer());
275e3b55780SDimitry Andric   switch (Type) {
276e3b55780SDimitry Andric   case file_magic::bitcode:
277e3b55780SDimitry Andric     return extractFromBitcode(Buffer, Binaries);
278e3b55780SDimitry Andric   case file_magic::elf_relocatable:
279e3b55780SDimitry Andric   case file_magic::elf_executable:
280e3b55780SDimitry Andric   case file_magic::elf_shared_object:
281e3b55780SDimitry Andric   case file_magic::coff_object: {
282e3b55780SDimitry Andric     Expected<std::unique_ptr<ObjectFile>> ObjFile =
283e3b55780SDimitry Andric         ObjectFile::createObjectFile(Buffer, Type);
284e3b55780SDimitry Andric     if (!ObjFile)
285e3b55780SDimitry Andric       return ObjFile.takeError();
286e3b55780SDimitry Andric     return extractFromObject(*ObjFile->get(), Binaries);
287e3b55780SDimitry Andric   }
288e3b55780SDimitry Andric   case file_magic::archive: {
289e3b55780SDimitry Andric     Expected<std::unique_ptr<llvm::object::Archive>> LibFile =
290e3b55780SDimitry Andric         object::Archive::create(Buffer);
291e3b55780SDimitry Andric     if (!LibFile)
292e3b55780SDimitry Andric       return LibFile.takeError();
293e3b55780SDimitry Andric     return extractFromArchive(*LibFile->get(), Binaries);
294e3b55780SDimitry Andric   }
295e3b55780SDimitry Andric   case file_magic::offload_binary:
296e3b55780SDimitry Andric     return extractOffloadFiles(Buffer, Binaries);
297e3b55780SDimitry Andric   default:
298e3b55780SDimitry Andric     return Error::success();
299e3b55780SDimitry Andric   }
300e3b55780SDimitry Andric }
301e3b55780SDimitry Andric 
getOffloadKind(StringRef Name)302145449b1SDimitry Andric OffloadKind object::getOffloadKind(StringRef Name) {
303145449b1SDimitry Andric   return llvm::StringSwitch<OffloadKind>(Name)
304145449b1SDimitry Andric       .Case("openmp", OFK_OpenMP)
305145449b1SDimitry Andric       .Case("cuda", OFK_Cuda)
306145449b1SDimitry Andric       .Case("hip", OFK_HIP)
307145449b1SDimitry Andric       .Default(OFK_None);
308145449b1SDimitry Andric }
309145449b1SDimitry Andric 
getOffloadKindName(OffloadKind Kind)310145449b1SDimitry Andric StringRef object::getOffloadKindName(OffloadKind Kind) {
311145449b1SDimitry Andric   switch (Kind) {
312145449b1SDimitry Andric   case OFK_OpenMP:
313145449b1SDimitry Andric     return "openmp";
314145449b1SDimitry Andric   case OFK_Cuda:
315145449b1SDimitry Andric     return "cuda";
316145449b1SDimitry Andric   case OFK_HIP:
317145449b1SDimitry Andric     return "hip";
318145449b1SDimitry Andric   default:
319145449b1SDimitry Andric     return "none";
320145449b1SDimitry Andric   }
321145449b1SDimitry Andric }
322145449b1SDimitry Andric 
getImageKind(StringRef Name)323145449b1SDimitry Andric ImageKind object::getImageKind(StringRef Name) {
324145449b1SDimitry Andric   return llvm::StringSwitch<ImageKind>(Name)
325145449b1SDimitry Andric       .Case("o", IMG_Object)
326145449b1SDimitry Andric       .Case("bc", IMG_Bitcode)
327145449b1SDimitry Andric       .Case("cubin", IMG_Cubin)
328145449b1SDimitry Andric       .Case("fatbin", IMG_Fatbinary)
329145449b1SDimitry Andric       .Case("s", IMG_PTX)
330145449b1SDimitry Andric       .Default(IMG_None);
331145449b1SDimitry Andric }
332145449b1SDimitry Andric 
getImageKindName(ImageKind Kind)333145449b1SDimitry Andric StringRef object::getImageKindName(ImageKind Kind) {
334145449b1SDimitry Andric   switch (Kind) {
335145449b1SDimitry Andric   case IMG_Object:
336145449b1SDimitry Andric     return "o";
337145449b1SDimitry Andric   case IMG_Bitcode:
338145449b1SDimitry Andric     return "bc";
339145449b1SDimitry Andric   case IMG_Cubin:
340145449b1SDimitry Andric     return "cubin";
341145449b1SDimitry Andric   case IMG_Fatbinary:
342145449b1SDimitry Andric     return "fatbin";
343145449b1SDimitry Andric   case IMG_PTX:
344145449b1SDimitry Andric     return "s";
345145449b1SDimitry Andric   default:
346145449b1SDimitry Andric     return "";
347145449b1SDimitry Andric   }
348145449b1SDimitry Andric }
3494df029ccSDimitry Andric 
areTargetsCompatible(const OffloadFile::TargetID & LHS,const OffloadFile::TargetID & RHS)3504df029ccSDimitry Andric bool object::areTargetsCompatible(const OffloadFile::TargetID &LHS,
3514df029ccSDimitry Andric                                   const OffloadFile::TargetID &RHS) {
3524df029ccSDimitry Andric   // Exact matches are not considered compatible because they are the same
3534df029ccSDimitry Andric   // target. We are interested in different targets that are compatible.
3544df029ccSDimitry Andric   if (LHS == RHS)
3554df029ccSDimitry Andric     return false;
3564df029ccSDimitry Andric 
3574df029ccSDimitry Andric   // The triples must match at all times.
3584df029ccSDimitry Andric   if (LHS.first != RHS.first)
3594df029ccSDimitry Andric     return false;
3604df029ccSDimitry Andric 
361ac9a064cSDimitry Andric   // If the architecture is "all" we assume it is always compatible.
362ac9a064cSDimitry Andric   if (LHS.second == "generic" || RHS.second == "generic")
363ac9a064cSDimitry Andric     return true;
364ac9a064cSDimitry Andric 
3654df029ccSDimitry Andric   // Only The AMDGPU target requires additional checks.
3664df029ccSDimitry Andric   llvm::Triple T(LHS.first);
3674df029ccSDimitry Andric   if (!T.isAMDGPU())
3684df029ccSDimitry Andric     return false;
3694df029ccSDimitry Andric 
3704df029ccSDimitry Andric   // The base processor must always match.
3714df029ccSDimitry Andric   if (LHS.second.split(":").first != RHS.second.split(":").first)
3724df029ccSDimitry Andric     return false;
3734df029ccSDimitry Andric 
3744df029ccSDimitry Andric   // Check combintions of on / off features that must match.
3754df029ccSDimitry Andric   if (LHS.second.contains("xnack+") && RHS.second.contains("xnack-"))
3764df029ccSDimitry Andric     return false;
3774df029ccSDimitry Andric   if (LHS.second.contains("xnack-") && RHS.second.contains("xnack+"))
3784df029ccSDimitry Andric     return false;
3794df029ccSDimitry Andric   if (LHS.second.contains("sramecc-") && RHS.second.contains("sramecc+"))
3804df029ccSDimitry Andric     return false;
3814df029ccSDimitry Andric   if (LHS.second.contains("sramecc+") && RHS.second.contains("sramecc-"))
3824df029ccSDimitry Andric     return false;
3834df029ccSDimitry Andric   return true;
3844df029ccSDimitry Andric }
385