xref: /llvm-project/llvm/lib/Object/OffloadBinary.cpp (revision e9c8106a90d49e75bac87341ade57c6049357a97)
1e471ba3dSJoseph Huber //===- Offloading.cpp - Utilities for handling offloading code  -*- C++ -*-===//
2e471ba3dSJoseph Huber //
3e471ba3dSJoseph Huber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e471ba3dSJoseph Huber // See https://llvm.org/LICENSE.txt for license information.
5e471ba3dSJoseph Huber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e471ba3dSJoseph Huber //
7e471ba3dSJoseph Huber //===----------------------------------------------------------------------===//
8e471ba3dSJoseph Huber 
9e471ba3dSJoseph Huber #include "llvm/Object/OffloadBinary.h"
10e471ba3dSJoseph Huber 
11e471ba3dSJoseph Huber #include "llvm/ADT/StringSwitch.h"
12afd2f7e9SJoseph Huber #include "llvm/BinaryFormat/Magic.h"
135dbc7cf7SJoseph Huber #include "llvm/IR/Constants.h"
145dbc7cf7SJoseph Huber #include "llvm/IR/Module.h"
155dbc7cf7SJoseph Huber #include "llvm/IRReader/IRReader.h"
16e471ba3dSJoseph Huber #include "llvm/MC/StringTableBuilder.h"
175dbc7cf7SJoseph Huber #include "llvm/Object/Archive.h"
185dbc7cf7SJoseph Huber #include "llvm/Object/Binary.h"
195dbc7cf7SJoseph Huber #include "llvm/Object/ELFObjectFile.h"
20e471ba3dSJoseph Huber #include "llvm/Object/Error.h"
215dbc7cf7SJoseph Huber #include "llvm/Object/IRObjectFile.h"
225dbc7cf7SJoseph Huber #include "llvm/Object/ObjectFile.h"
23ccf7dd5eSJoseph Huber #include "llvm/Support/Alignment.h"
245dbc7cf7SJoseph Huber #include "llvm/Support/SourceMgr.h"
25e471ba3dSJoseph Huber 
262108f7a2SFangrui Song using namespace llvm;
272108f7a2SFangrui Song using namespace llvm::object;
28afd2f7e9SJoseph Huber 
295dbc7cf7SJoseph Huber namespace {
305dbc7cf7SJoseph Huber 
315dbc7cf7SJoseph Huber /// Attempts to extract all the embedded device images contained inside the
325dbc7cf7SJoseph Huber /// buffer \p Contents. The buffer is expected to contain a valid offloading
335dbc7cf7SJoseph Huber /// binary format.
345dbc7cf7SJoseph Huber Error extractOffloadFiles(MemoryBufferRef Contents,
355dbc7cf7SJoseph Huber                           SmallVectorImpl<OffloadFile> &Binaries) {
365dbc7cf7SJoseph Huber   uint64_t Offset = 0;
375dbc7cf7SJoseph Huber   // There could be multiple offloading binaries stored at this section.
385dbc7cf7SJoseph Huber   while (Offset < Contents.getBuffer().size()) {
395dbc7cf7SJoseph Huber     std::unique_ptr<MemoryBuffer> Buffer =
405dbc7cf7SJoseph Huber         MemoryBuffer::getMemBuffer(Contents.getBuffer().drop_front(Offset), "",
415dbc7cf7SJoseph Huber                                    /*RequiresNullTerminator*/ false);
423384f05aSJoseph Huber     if (!isAddrAligned(Align(OffloadBinary::getAlignment()),
433384f05aSJoseph Huber                        Buffer->getBufferStart()))
443384f05aSJoseph Huber       Buffer = MemoryBuffer::getMemBufferCopy(Buffer->getBuffer(),
453384f05aSJoseph Huber                                               Buffer->getBufferIdentifier());
465dbc7cf7SJoseph Huber     auto BinaryOrErr = OffloadBinary::create(*Buffer);
475dbc7cf7SJoseph Huber     if (!BinaryOrErr)
485dbc7cf7SJoseph Huber       return BinaryOrErr.takeError();
495dbc7cf7SJoseph Huber     OffloadBinary &Binary = **BinaryOrErr;
505dbc7cf7SJoseph Huber 
515dbc7cf7SJoseph Huber     // Create a new owned binary with a copy of the original memory.
525dbc7cf7SJoseph Huber     std::unique_ptr<MemoryBuffer> BufferCopy = MemoryBuffer::getMemBufferCopy(
535dbc7cf7SJoseph Huber         Binary.getData().take_front(Binary.getSize()),
545dbc7cf7SJoseph Huber         Contents.getBufferIdentifier());
555dbc7cf7SJoseph Huber     auto NewBinaryOrErr = OffloadBinary::create(*BufferCopy);
565dbc7cf7SJoseph Huber     if (!NewBinaryOrErr)
575dbc7cf7SJoseph Huber       return NewBinaryOrErr.takeError();
585dbc7cf7SJoseph Huber     Binaries.emplace_back(std::move(*NewBinaryOrErr), std::move(BufferCopy));
595dbc7cf7SJoseph Huber 
605dbc7cf7SJoseph Huber     Offset += Binary.getSize();
615dbc7cf7SJoseph Huber   }
625dbc7cf7SJoseph Huber 
635dbc7cf7SJoseph Huber   return Error::success();
645dbc7cf7SJoseph Huber }
655dbc7cf7SJoseph Huber 
665dbc7cf7SJoseph Huber // Extract offloading binaries from an Object file \p Obj.
678298f0b7SJoseph Huber Error extractFromObject(const ObjectFile &Obj,
685dbc7cf7SJoseph Huber                         SmallVectorImpl<OffloadFile> &Binaries) {
698298f0b7SJoseph Huber   assert((Obj.isELF() || Obj.isCOFF()) && "Invalid file type");
708298f0b7SJoseph Huber 
718298f0b7SJoseph Huber   for (SectionRef Sec : Obj.sections()) {
728298f0b7SJoseph Huber     // ELF files contain a section with the LLVM_OFFLOADING type.
738298f0b7SJoseph Huber     if (Obj.isELF() &&
748298f0b7SJoseph Huber         static_cast<ELFSectionRef>(Sec).getType() != ELF::SHT_LLVM_OFFLOADING)
755dbc7cf7SJoseph Huber       continue;
765dbc7cf7SJoseph Huber 
778298f0b7SJoseph Huber     // COFF has no section types so we rely on the name of the section.
788298f0b7SJoseph Huber     if (Obj.isCOFF()) {
798298f0b7SJoseph Huber       Expected<StringRef> NameOrErr = Sec.getName();
808298f0b7SJoseph Huber       if (!NameOrErr)
818298f0b7SJoseph Huber         return NameOrErr.takeError();
828298f0b7SJoseph Huber 
835c840542SJoseph Huber       if (!NameOrErr->starts_with(".llvm.offloading"))
848298f0b7SJoseph Huber         continue;
858298f0b7SJoseph Huber     }
868298f0b7SJoseph Huber 
875dbc7cf7SJoseph Huber     Expected<StringRef> Buffer = Sec.getContents();
885dbc7cf7SJoseph Huber     if (!Buffer)
895dbc7cf7SJoseph Huber       return Buffer.takeError();
905dbc7cf7SJoseph Huber 
915dbc7cf7SJoseph Huber     MemoryBufferRef Contents(*Buffer, Obj.getFileName());
925dbc7cf7SJoseph Huber     if (Error Err = extractOffloadFiles(Contents, Binaries))
935dbc7cf7SJoseph Huber       return Err;
945dbc7cf7SJoseph Huber   }
955dbc7cf7SJoseph Huber 
965dbc7cf7SJoseph Huber   return Error::success();
975dbc7cf7SJoseph Huber }
985dbc7cf7SJoseph Huber 
995dbc7cf7SJoseph Huber Error extractFromBitcode(MemoryBufferRef Buffer,
1005dbc7cf7SJoseph Huber                          SmallVectorImpl<OffloadFile> &Binaries) {
1015dbc7cf7SJoseph Huber   LLVMContext Context;
1025dbc7cf7SJoseph Huber   SMDiagnostic Err;
1035dbc7cf7SJoseph Huber   std::unique_ptr<Module> M = getLazyIRModule(
1045dbc7cf7SJoseph Huber       MemoryBuffer::getMemBuffer(Buffer, /*RequiresNullTerminator=*/false), Err,
1055dbc7cf7SJoseph Huber       Context);
1065dbc7cf7SJoseph Huber   if (!M)
1075dbc7cf7SJoseph Huber     return createStringError(inconvertibleErrorCode(),
1085dbc7cf7SJoseph Huber                              "Failed to create module");
1095dbc7cf7SJoseph Huber 
1105dbc7cf7SJoseph Huber   // Extract offloading data from globals referenced by the
1115dbc7cf7SJoseph Huber   // `llvm.embedded.object` metadata with the `.llvm.offloading` section.
1125dbc7cf7SJoseph Huber   auto *MD = M->getNamedMetadata("llvm.embedded.objects");
1135dbc7cf7SJoseph Huber   if (!MD)
1145dbc7cf7SJoseph Huber     return Error::success();
1155dbc7cf7SJoseph Huber 
1165dbc7cf7SJoseph Huber   for (const MDNode *Op : MD->operands()) {
1175dbc7cf7SJoseph Huber     if (Op->getNumOperands() < 2)
1185dbc7cf7SJoseph Huber       continue;
1195dbc7cf7SJoseph Huber 
1205dbc7cf7SJoseph Huber     MDString *SectionID = dyn_cast<MDString>(Op->getOperand(1));
1215dbc7cf7SJoseph Huber     if (!SectionID || SectionID->getString() != ".llvm.offloading")
1225dbc7cf7SJoseph Huber       continue;
1235dbc7cf7SJoseph Huber 
1245dbc7cf7SJoseph Huber     GlobalVariable *GV =
1255dbc7cf7SJoseph Huber         mdconst::dyn_extract_or_null<GlobalVariable>(Op->getOperand(0));
1265dbc7cf7SJoseph Huber     if (!GV)
1275dbc7cf7SJoseph Huber       continue;
1285dbc7cf7SJoseph Huber 
1295dbc7cf7SJoseph Huber     auto *CDS = dyn_cast<ConstantDataSequential>(GV->getInitializer());
1305dbc7cf7SJoseph Huber     if (!CDS)
1315dbc7cf7SJoseph Huber       continue;
1325dbc7cf7SJoseph Huber 
1335dbc7cf7SJoseph Huber     MemoryBufferRef Contents(CDS->getAsString(), M->getName());
1345dbc7cf7SJoseph Huber     if (Error Err = extractOffloadFiles(Contents, Binaries))
1355dbc7cf7SJoseph Huber       return Err;
1365dbc7cf7SJoseph Huber   }
1375dbc7cf7SJoseph Huber 
1385dbc7cf7SJoseph Huber   return Error::success();
1395dbc7cf7SJoseph Huber }
1405dbc7cf7SJoseph Huber 
1415dbc7cf7SJoseph Huber Error extractFromArchive(const Archive &Library,
1425dbc7cf7SJoseph Huber                          SmallVectorImpl<OffloadFile> &Binaries) {
1435dbc7cf7SJoseph Huber   // Try to extract device code from each file stored in the static archive.
1445dbc7cf7SJoseph Huber   Error Err = Error::success();
1455dbc7cf7SJoseph Huber   for (auto Child : Library.children(Err)) {
1465dbc7cf7SJoseph Huber     auto ChildBufferOrErr = Child.getMemoryBufferRef();
1475dbc7cf7SJoseph Huber     if (!ChildBufferOrErr)
1485dbc7cf7SJoseph Huber       return ChildBufferOrErr.takeError();
1495dbc7cf7SJoseph Huber     std::unique_ptr<MemoryBuffer> ChildBuffer =
1505dbc7cf7SJoseph Huber         MemoryBuffer::getMemBuffer(*ChildBufferOrErr, false);
1515dbc7cf7SJoseph Huber 
1525dbc7cf7SJoseph Huber     // Check if the buffer has the required alignment.
1535dbc7cf7SJoseph Huber     if (!isAddrAligned(Align(OffloadBinary::getAlignment()),
1545dbc7cf7SJoseph Huber                        ChildBuffer->getBufferStart()))
1555dbc7cf7SJoseph Huber       ChildBuffer = MemoryBuffer::getMemBufferCopy(
1565dbc7cf7SJoseph Huber           ChildBufferOrErr->getBuffer(),
1575dbc7cf7SJoseph Huber           ChildBufferOrErr->getBufferIdentifier());
1585dbc7cf7SJoseph Huber 
1595dbc7cf7SJoseph Huber     if (Error Err = extractOffloadBinaries(*ChildBuffer, Binaries))
1605dbc7cf7SJoseph Huber       return Err;
1615dbc7cf7SJoseph Huber   }
1625dbc7cf7SJoseph Huber 
1635dbc7cf7SJoseph Huber   if (Err)
1645dbc7cf7SJoseph Huber     return Err;
1655dbc7cf7SJoseph Huber   return Error::success();
1665dbc7cf7SJoseph Huber }
1675dbc7cf7SJoseph Huber 
1685dbc7cf7SJoseph Huber } // namespace
1695dbc7cf7SJoseph Huber 
170e471ba3dSJoseph Huber Expected<std::unique_ptr<OffloadBinary>>
171e471ba3dSJoseph Huber OffloadBinary::create(MemoryBufferRef Buf) {
172e471ba3dSJoseph Huber   if (Buf.getBufferSize() < sizeof(Header) + sizeof(Entry))
173afd2f7e9SJoseph Huber     return errorCodeToError(object_error::parse_failed);
174e471ba3dSJoseph Huber 
175e471ba3dSJoseph Huber   // Check for 0x10FF1OAD magic bytes.
176afd2f7e9SJoseph Huber   if (identify_magic(Buf.getBuffer()) != file_magic::offload_binary)
177afd2f7e9SJoseph Huber     return errorCodeToError(object_error::parse_failed);
178e471ba3dSJoseph Huber 
179ccf7dd5eSJoseph Huber   // Make sure that the data has sufficient alignment.
180ccf7dd5eSJoseph Huber   if (!isAddrAligned(Align(getAlignment()), Buf.getBufferStart()))
181ccf7dd5eSJoseph Huber     return errorCodeToError(object_error::parse_failed);
182ccf7dd5eSJoseph Huber 
183e471ba3dSJoseph Huber   const char *Start = Buf.getBufferStart();
184e471ba3dSJoseph Huber   const Header *TheHeader = reinterpret_cast<const Header *>(Start);
1851dcbe03cSJoseph Huber   if (TheHeader->Version != OffloadBinary::Version)
1861dcbe03cSJoseph Huber     return errorCodeToError(object_error::parse_failed);
1871dcbe03cSJoseph Huber 
1881dcbe03cSJoseph Huber   if (TheHeader->Size > Buf.getBufferSize() ||
1896f44bb77SAntonio Frighetto       TheHeader->Size < sizeof(Entry) || TheHeader->Size < sizeof(Header))
1906f44bb77SAntonio Frighetto     return errorCodeToError(object_error::unexpected_eof);
1916f44bb77SAntonio Frighetto 
1926f44bb77SAntonio Frighetto   if (TheHeader->EntryOffset > TheHeader->Size - sizeof(Entry) ||
1931dcbe03cSJoseph Huber       TheHeader->EntrySize > TheHeader->Size - sizeof(Header))
1941dcbe03cSJoseph Huber     return errorCodeToError(object_error::unexpected_eof);
1951dcbe03cSJoseph Huber 
196e471ba3dSJoseph Huber   const Entry *TheEntry =
197e471ba3dSJoseph Huber       reinterpret_cast<const Entry *>(&Start[TheHeader->EntryOffset]);
198e471ba3dSJoseph Huber 
1991dcbe03cSJoseph Huber   if (TheEntry->ImageOffset > Buf.getBufferSize() ||
2004e2a0092SJoseph Huber       TheEntry->StringOffset > Buf.getBufferSize())
2014e2a0092SJoseph Huber     return errorCodeToError(object_error::unexpected_eof);
2024e2a0092SJoseph Huber 
203e471ba3dSJoseph Huber   return std::unique_ptr<OffloadBinary>(
204afd2f7e9SJoseph Huber       new OffloadBinary(Buf, TheHeader, TheEntry));
205e471ba3dSJoseph Huber }
206e471ba3dSJoseph Huber 
207f93c271dSFangrui Song SmallString<0> OffloadBinary::write(const OffloadingImage &OffloadingData) {
208e471ba3dSJoseph Huber   // Create a null-terminated string table with all the used strings.
209e471ba3dSJoseph Huber   StringTableBuilder StrTab(StringTableBuilder::ELF);
210e471ba3dSJoseph Huber   for (auto &KeyAndValue : OffloadingData.StringData) {
211ecb1d844SFangrui Song     StrTab.add(KeyAndValue.first);
212ecb1d844SFangrui Song     StrTab.add(KeyAndValue.second);
213e471ba3dSJoseph Huber   }
214e471ba3dSJoseph Huber   StrTab.finalize();
215e471ba3dSJoseph Huber 
216e471ba3dSJoseph Huber   uint64_t StringEntrySize =
217e471ba3dSJoseph Huber       sizeof(StringEntry) * OffloadingData.StringData.size();
218e471ba3dSJoseph Huber 
2199db2f323SJoseph Huber   // Make sure the image we're wrapping around is aligned as well.
2209db2f323SJoseph Huber   uint64_t BinaryDataSize = alignTo(sizeof(Header) + sizeof(Entry) +
2219db2f323SJoseph Huber                                         StringEntrySize + StrTab.getSize(),
2229db2f323SJoseph Huber                                     getAlignment());
2239db2f323SJoseph Huber 
224e471ba3dSJoseph Huber   // Create the header and fill in the offsets. The entry will be directly
225e471ba3dSJoseph Huber   // placed after the header in memory. Align the size to the alignment of the
226e471ba3dSJoseph Huber   // header so this can be placed contiguously in a single section.
227e471ba3dSJoseph Huber   Header TheHeader;
2289db2f323SJoseph Huber   TheHeader.Size = alignTo(
229f06731e3SJoseph Huber       BinaryDataSize + OffloadingData.Image->getBufferSize(), getAlignment());
230e471ba3dSJoseph Huber   TheHeader.EntryOffset = sizeof(Header);
231e471ba3dSJoseph Huber   TheHeader.EntrySize = sizeof(Entry);
232e471ba3dSJoseph Huber 
233e471ba3dSJoseph Huber   // Create the entry using the string table offsets. The string table will be
234e471ba3dSJoseph Huber   // placed directly after the entry in memory, and the image after that.
235e471ba3dSJoseph Huber   Entry TheEntry;
236e471ba3dSJoseph Huber   TheEntry.TheImageKind = OffloadingData.TheImageKind;
237e471ba3dSJoseph Huber   TheEntry.TheOffloadKind = OffloadingData.TheOffloadKind;
238e471ba3dSJoseph Huber   TheEntry.Flags = OffloadingData.Flags;
239e471ba3dSJoseph Huber   TheEntry.StringOffset = sizeof(Header) + sizeof(Entry);
240e471ba3dSJoseph Huber   TheEntry.NumStrings = OffloadingData.StringData.size();
241e471ba3dSJoseph Huber 
2429db2f323SJoseph Huber   TheEntry.ImageOffset = BinaryDataSize;
243f06731e3SJoseph Huber   TheEntry.ImageSize = OffloadingData.Image->getBufferSize();
244e471ba3dSJoseph Huber 
245f93c271dSFangrui Song   SmallString<0> Data;
2464e2a0092SJoseph Huber   Data.reserve(TheHeader.Size);
247e471ba3dSJoseph Huber   raw_svector_ostream OS(Data);
248e471ba3dSJoseph Huber   OS << StringRef(reinterpret_cast<char *>(&TheHeader), sizeof(Header));
249e471ba3dSJoseph Huber   OS << StringRef(reinterpret_cast<char *>(&TheEntry), sizeof(Entry));
250e471ba3dSJoseph Huber   for (auto &KeyAndValue : OffloadingData.StringData) {
251e471ba3dSJoseph Huber     uint64_t Offset = sizeof(Header) + sizeof(Entry) + StringEntrySize;
252ecb1d844SFangrui Song     StringEntry Map{Offset + StrTab.getOffset(KeyAndValue.first),
253ecb1d844SFangrui Song                     Offset + StrTab.getOffset(KeyAndValue.second)};
254e471ba3dSJoseph Huber     OS << StringRef(reinterpret_cast<char *>(&Map), sizeof(StringEntry));
255e471ba3dSJoseph Huber   }
256e471ba3dSJoseph Huber   StrTab.write(OS);
2579db2f323SJoseph Huber   // Add padding to required image alignment.
2589db2f323SJoseph Huber   OS.write_zeros(TheEntry.ImageOffset - OS.tell());
259f06731e3SJoseph Huber   OS << OffloadingData.Image->getBuffer();
260e471ba3dSJoseph Huber 
261e471ba3dSJoseph Huber   // Add final padding to required alignment.
262e471ba3dSJoseph Huber   assert(TheHeader.Size >= OS.tell() && "Too much data written?");
263e471ba3dSJoseph Huber   OS.write_zeros(TheHeader.Size - OS.tell());
264e471ba3dSJoseph Huber   assert(TheHeader.Size == OS.tell() && "Size mismatch");
265e471ba3dSJoseph Huber 
266f93c271dSFangrui Song   return Data;
267e471ba3dSJoseph Huber }
268e471ba3dSJoseph Huber 
2695dbc7cf7SJoseph Huber Error object::extractOffloadBinaries(MemoryBufferRef Buffer,
2705dbc7cf7SJoseph Huber                                      SmallVectorImpl<OffloadFile> &Binaries) {
2715dbc7cf7SJoseph Huber   file_magic Type = identify_magic(Buffer.getBuffer());
2725dbc7cf7SJoseph Huber   switch (Type) {
2735dbc7cf7SJoseph Huber   case file_magic::bitcode:
2745dbc7cf7SJoseph Huber     return extractFromBitcode(Buffer, Binaries);
2753384f05aSJoseph Huber   case file_magic::elf_relocatable:
2763384f05aSJoseph Huber   case file_magic::elf_executable:
2778298f0b7SJoseph Huber   case file_magic::elf_shared_object:
2788298f0b7SJoseph Huber   case file_magic::coff_object: {
2795dbc7cf7SJoseph Huber     Expected<std::unique_ptr<ObjectFile>> ObjFile =
2805dbc7cf7SJoseph Huber         ObjectFile::createObjectFile(Buffer, Type);
2815dbc7cf7SJoseph Huber     if (!ObjFile)
2825dbc7cf7SJoseph Huber       return ObjFile.takeError();
2838298f0b7SJoseph Huber     return extractFromObject(*ObjFile->get(), Binaries);
2845dbc7cf7SJoseph Huber   }
2855dbc7cf7SJoseph Huber   case file_magic::archive: {
2865dbc7cf7SJoseph Huber     Expected<std::unique_ptr<llvm::object::Archive>> LibFile =
2875dbc7cf7SJoseph Huber         object::Archive::create(Buffer);
2885dbc7cf7SJoseph Huber     if (!LibFile)
2895dbc7cf7SJoseph Huber       return LibFile.takeError();
2905dbc7cf7SJoseph Huber     return extractFromArchive(*LibFile->get(), Binaries);
2915dbc7cf7SJoseph Huber   }
2925dbc7cf7SJoseph Huber   case file_magic::offload_binary:
2935dbc7cf7SJoseph Huber     return extractOffloadFiles(Buffer, Binaries);
2945dbc7cf7SJoseph Huber   default:
2955dbc7cf7SJoseph Huber     return Error::success();
2965dbc7cf7SJoseph Huber   }
2975dbc7cf7SJoseph Huber }
2985dbc7cf7SJoseph Huber 
2992108f7a2SFangrui Song OffloadKind object::getOffloadKind(StringRef Name) {
300e471ba3dSJoseph Huber   return llvm::StringSwitch<OffloadKind>(Name)
301e471ba3dSJoseph Huber       .Case("openmp", OFK_OpenMP)
302e471ba3dSJoseph Huber       .Case("cuda", OFK_Cuda)
303e471ba3dSJoseph Huber       .Case("hip", OFK_HIP)
304e471ba3dSJoseph Huber       .Default(OFK_None);
305e471ba3dSJoseph Huber }
306e471ba3dSJoseph Huber 
3072108f7a2SFangrui Song StringRef object::getOffloadKindName(OffloadKind Kind) {
308e471ba3dSJoseph Huber   switch (Kind) {
309e471ba3dSJoseph Huber   case OFK_OpenMP:
310e471ba3dSJoseph Huber     return "openmp";
311e471ba3dSJoseph Huber   case OFK_Cuda:
312e471ba3dSJoseph Huber     return "cuda";
313e471ba3dSJoseph Huber   case OFK_HIP:
314e471ba3dSJoseph Huber     return "hip";
315e471ba3dSJoseph Huber   default:
316e471ba3dSJoseph Huber     return "none";
317e471ba3dSJoseph Huber   }
318e471ba3dSJoseph Huber }
319e471ba3dSJoseph Huber 
3202108f7a2SFangrui Song ImageKind object::getImageKind(StringRef Name) {
321e471ba3dSJoseph Huber   return llvm::StringSwitch<ImageKind>(Name)
322e471ba3dSJoseph Huber       .Case("o", IMG_Object)
323e471ba3dSJoseph Huber       .Case("bc", IMG_Bitcode)
324e471ba3dSJoseph Huber       .Case("cubin", IMG_Cubin)
325e471ba3dSJoseph Huber       .Case("fatbin", IMG_Fatbinary)
326e471ba3dSJoseph Huber       .Case("s", IMG_PTX)
327e471ba3dSJoseph Huber       .Default(IMG_None);
328e471ba3dSJoseph Huber }
329e471ba3dSJoseph Huber 
3302108f7a2SFangrui Song StringRef object::getImageKindName(ImageKind Kind) {
331e471ba3dSJoseph Huber   switch (Kind) {
332e471ba3dSJoseph Huber   case IMG_Object:
333e471ba3dSJoseph Huber     return "o";
334e471ba3dSJoseph Huber   case IMG_Bitcode:
335e471ba3dSJoseph Huber     return "bc";
336e471ba3dSJoseph Huber   case IMG_Cubin:
337e471ba3dSJoseph Huber     return "cubin";
338e471ba3dSJoseph Huber   case IMG_Fatbinary:
339e471ba3dSJoseph Huber     return "fatbin";
340e471ba3dSJoseph Huber   case IMG_PTX:
341e471ba3dSJoseph Huber     return "s";
342e471ba3dSJoseph Huber   default:
343e471ba3dSJoseph Huber     return "";
344e471ba3dSJoseph Huber   }
345e471ba3dSJoseph Huber }
34612c90bd6SJoseph Huber 
34712c90bd6SJoseph Huber bool object::areTargetsCompatible(const OffloadFile::TargetID &LHS,
34812c90bd6SJoseph Huber                                   const OffloadFile::TargetID &RHS) {
34912c90bd6SJoseph Huber   // Exact matches are not considered compatible because they are the same
35012c90bd6SJoseph Huber   // target. We are interested in different targets that are compatible.
35112c90bd6SJoseph Huber   if (LHS == RHS)
35212c90bd6SJoseph Huber     return false;
35312c90bd6SJoseph Huber 
35412c90bd6SJoseph Huber   // The triples must match at all times.
35512c90bd6SJoseph Huber   if (LHS.first != RHS.first)
35612c90bd6SJoseph Huber     return false;
35712c90bd6SJoseph Huber 
35842230e21SJoseph Huber   // If the architecture is "all" we assume it is always compatible.
359*bb6df080SKazu Hirata   if (LHS.second == "generic" || RHS.second == "generic")
36042230e21SJoseph Huber     return true;
36142230e21SJoseph Huber 
36212c90bd6SJoseph Huber   // Only The AMDGPU target requires additional checks.
36312c90bd6SJoseph Huber   llvm::Triple T(LHS.first);
36412c90bd6SJoseph Huber   if (!T.isAMDGPU())
36512c90bd6SJoseph Huber     return false;
36612c90bd6SJoseph Huber 
36712c90bd6SJoseph Huber   // The base processor must always match.
36812c90bd6SJoseph Huber   if (LHS.second.split(":").first != RHS.second.split(":").first)
36912c90bd6SJoseph Huber     return false;
37012c90bd6SJoseph Huber 
37112c90bd6SJoseph Huber   // Check combintions of on / off features that must match.
37212c90bd6SJoseph Huber   if (LHS.second.contains("xnack+") && RHS.second.contains("xnack-"))
37312c90bd6SJoseph Huber     return false;
37412c90bd6SJoseph Huber   if (LHS.second.contains("xnack-") && RHS.second.contains("xnack+"))
37512c90bd6SJoseph Huber     return false;
37612c90bd6SJoseph Huber   if (LHS.second.contains("sramecc-") && RHS.second.contains("sramecc+"))
37712c90bd6SJoseph Huber     return false;
37812c90bd6SJoseph Huber   if (LHS.second.contains("sramecc+") && RHS.second.contains("sramecc-"))
37912c90bd6SJoseph Huber     return false;
38012c90bd6SJoseph Huber   return true;
38112c90bd6SJoseph Huber }
382