Path: blob/main/contrib/llvm-project/llvm/lib/Object/OffloadBinary.cpp
35232 views
//===- Offloading.cpp - Utilities for handling offloading code -*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/Object/OffloadBinary.h"910#include "llvm/ADT/StringSwitch.h"11#include "llvm/BinaryFormat/Magic.h"12#include "llvm/IR/Constants.h"13#include "llvm/IR/Module.h"14#include "llvm/IRReader/IRReader.h"15#include "llvm/MC/StringTableBuilder.h"16#include "llvm/Object/Archive.h"17#include "llvm/Object/ArchiveWriter.h"18#include "llvm/Object/Binary.h"19#include "llvm/Object/COFF.h"20#include "llvm/Object/ELFObjectFile.h"21#include "llvm/Object/Error.h"22#include "llvm/Object/IRObjectFile.h"23#include "llvm/Object/ObjectFile.h"24#include "llvm/Support/Alignment.h"25#include "llvm/Support/FileOutputBuffer.h"26#include "llvm/Support/SourceMgr.h"2728using namespace llvm;29using namespace llvm::object;3031namespace {3233/// Attempts to extract all the embedded device images contained inside the34/// buffer \p Contents. The buffer is expected to contain a valid offloading35/// binary format.36Error extractOffloadFiles(MemoryBufferRef Contents,37SmallVectorImpl<OffloadFile> &Binaries) {38uint64_t Offset = 0;39// There could be multiple offloading binaries stored at this section.40while (Offset < Contents.getBuffer().size()) {41std::unique_ptr<MemoryBuffer> Buffer =42MemoryBuffer::getMemBuffer(Contents.getBuffer().drop_front(Offset), "",43/*RequiresNullTerminator*/ false);44if (!isAddrAligned(Align(OffloadBinary::getAlignment()),45Buffer->getBufferStart()))46Buffer = MemoryBuffer::getMemBufferCopy(Buffer->getBuffer(),47Buffer->getBufferIdentifier());48auto BinaryOrErr = OffloadBinary::create(*Buffer);49if (!BinaryOrErr)50return BinaryOrErr.takeError();51OffloadBinary &Binary = **BinaryOrErr;5253// Create a new owned binary with a copy of the original memory.54std::unique_ptr<MemoryBuffer> BufferCopy = MemoryBuffer::getMemBufferCopy(55Binary.getData().take_front(Binary.getSize()),56Contents.getBufferIdentifier());57auto NewBinaryOrErr = OffloadBinary::create(*BufferCopy);58if (!NewBinaryOrErr)59return NewBinaryOrErr.takeError();60Binaries.emplace_back(std::move(*NewBinaryOrErr), std::move(BufferCopy));6162Offset += Binary.getSize();63}6465return Error::success();66}6768// Extract offloading binaries from an Object file \p Obj.69Error extractFromObject(const ObjectFile &Obj,70SmallVectorImpl<OffloadFile> &Binaries) {71assert((Obj.isELF() || Obj.isCOFF()) && "Invalid file type");7273for (SectionRef Sec : Obj.sections()) {74// ELF files contain a section with the LLVM_OFFLOADING type.75if (Obj.isELF() &&76static_cast<ELFSectionRef>(Sec).getType() != ELF::SHT_LLVM_OFFLOADING)77continue;7879// COFF has no section types so we rely on the name of the section.80if (Obj.isCOFF()) {81Expected<StringRef> NameOrErr = Sec.getName();82if (!NameOrErr)83return NameOrErr.takeError();8485if (!NameOrErr->starts_with(".llvm.offloading"))86continue;87}8889Expected<StringRef> Buffer = Sec.getContents();90if (!Buffer)91return Buffer.takeError();9293MemoryBufferRef Contents(*Buffer, Obj.getFileName());94if (Error Err = extractOffloadFiles(Contents, Binaries))95return Err;96}9798return Error::success();99}100101Error extractFromBitcode(MemoryBufferRef Buffer,102SmallVectorImpl<OffloadFile> &Binaries) {103LLVMContext Context;104SMDiagnostic Err;105std::unique_ptr<Module> M = getLazyIRModule(106MemoryBuffer::getMemBuffer(Buffer, /*RequiresNullTerminator=*/false), Err,107Context);108if (!M)109return createStringError(inconvertibleErrorCode(),110"Failed to create module");111112// Extract offloading data from globals referenced by the113// `llvm.embedded.object` metadata with the `.llvm.offloading` section.114auto *MD = M->getNamedMetadata("llvm.embedded.objects");115if (!MD)116return Error::success();117118for (const MDNode *Op : MD->operands()) {119if (Op->getNumOperands() < 2)120continue;121122MDString *SectionID = dyn_cast<MDString>(Op->getOperand(1));123if (!SectionID || SectionID->getString() != ".llvm.offloading")124continue;125126GlobalVariable *GV =127mdconst::dyn_extract_or_null<GlobalVariable>(Op->getOperand(0));128if (!GV)129continue;130131auto *CDS = dyn_cast<ConstantDataSequential>(GV->getInitializer());132if (!CDS)133continue;134135MemoryBufferRef Contents(CDS->getAsString(), M->getName());136if (Error Err = extractOffloadFiles(Contents, Binaries))137return Err;138}139140return Error::success();141}142143Error extractFromArchive(const Archive &Library,144SmallVectorImpl<OffloadFile> &Binaries) {145// Try to extract device code from each file stored in the static archive.146Error Err = Error::success();147for (auto Child : Library.children(Err)) {148auto ChildBufferOrErr = Child.getMemoryBufferRef();149if (!ChildBufferOrErr)150return ChildBufferOrErr.takeError();151std::unique_ptr<MemoryBuffer> ChildBuffer =152MemoryBuffer::getMemBuffer(*ChildBufferOrErr, false);153154// Check if the buffer has the required alignment.155if (!isAddrAligned(Align(OffloadBinary::getAlignment()),156ChildBuffer->getBufferStart()))157ChildBuffer = MemoryBuffer::getMemBufferCopy(158ChildBufferOrErr->getBuffer(),159ChildBufferOrErr->getBufferIdentifier());160161if (Error Err = extractOffloadBinaries(*ChildBuffer, Binaries))162return Err;163}164165if (Err)166return Err;167return Error::success();168}169170} // namespace171172Expected<std::unique_ptr<OffloadBinary>>173OffloadBinary::create(MemoryBufferRef Buf) {174if (Buf.getBufferSize() < sizeof(Header) + sizeof(Entry))175return errorCodeToError(object_error::parse_failed);176177// Check for 0x10FF1OAD magic bytes.178if (identify_magic(Buf.getBuffer()) != file_magic::offload_binary)179return errorCodeToError(object_error::parse_failed);180181// Make sure that the data has sufficient alignment.182if (!isAddrAligned(Align(getAlignment()), Buf.getBufferStart()))183return errorCodeToError(object_error::parse_failed);184185const char *Start = Buf.getBufferStart();186const Header *TheHeader = reinterpret_cast<const Header *>(Start);187if (TheHeader->Version != OffloadBinary::Version)188return errorCodeToError(object_error::parse_failed);189190if (TheHeader->Size > Buf.getBufferSize() ||191TheHeader->Size < sizeof(Entry) || TheHeader->Size < sizeof(Header))192return errorCodeToError(object_error::unexpected_eof);193194if (TheHeader->EntryOffset > TheHeader->Size - sizeof(Entry) ||195TheHeader->EntrySize > TheHeader->Size - sizeof(Header))196return errorCodeToError(object_error::unexpected_eof);197198const Entry *TheEntry =199reinterpret_cast<const Entry *>(&Start[TheHeader->EntryOffset]);200201if (TheEntry->ImageOffset > Buf.getBufferSize() ||202TheEntry->StringOffset > Buf.getBufferSize())203return errorCodeToError(object_error::unexpected_eof);204205return std::unique_ptr<OffloadBinary>(206new OffloadBinary(Buf, TheHeader, TheEntry));207}208209SmallString<0> OffloadBinary::write(const OffloadingImage &OffloadingData) {210// Create a null-terminated string table with all the used strings.211StringTableBuilder StrTab(StringTableBuilder::ELF);212for (auto &KeyAndValue : OffloadingData.StringData) {213StrTab.add(KeyAndValue.first);214StrTab.add(KeyAndValue.second);215}216StrTab.finalize();217218uint64_t StringEntrySize =219sizeof(StringEntry) * OffloadingData.StringData.size();220221// Make sure the image we're wrapping around is aligned as well.222uint64_t BinaryDataSize = alignTo(sizeof(Header) + sizeof(Entry) +223StringEntrySize + StrTab.getSize(),224getAlignment());225226// Create the header and fill in the offsets. The entry will be directly227// placed after the header in memory. Align the size to the alignment of the228// header so this can be placed contiguously in a single section.229Header TheHeader;230TheHeader.Size = alignTo(231BinaryDataSize + OffloadingData.Image->getBufferSize(), getAlignment());232TheHeader.EntryOffset = sizeof(Header);233TheHeader.EntrySize = sizeof(Entry);234235// Create the entry using the string table offsets. The string table will be236// placed directly after the entry in memory, and the image after that.237Entry TheEntry;238TheEntry.TheImageKind = OffloadingData.TheImageKind;239TheEntry.TheOffloadKind = OffloadingData.TheOffloadKind;240TheEntry.Flags = OffloadingData.Flags;241TheEntry.StringOffset = sizeof(Header) + sizeof(Entry);242TheEntry.NumStrings = OffloadingData.StringData.size();243244TheEntry.ImageOffset = BinaryDataSize;245TheEntry.ImageSize = OffloadingData.Image->getBufferSize();246247SmallString<0> Data;248Data.reserve(TheHeader.Size);249raw_svector_ostream OS(Data);250OS << StringRef(reinterpret_cast<char *>(&TheHeader), sizeof(Header));251OS << StringRef(reinterpret_cast<char *>(&TheEntry), sizeof(Entry));252for (auto &KeyAndValue : OffloadingData.StringData) {253uint64_t Offset = sizeof(Header) + sizeof(Entry) + StringEntrySize;254StringEntry Map{Offset + StrTab.getOffset(KeyAndValue.first),255Offset + StrTab.getOffset(KeyAndValue.second)};256OS << StringRef(reinterpret_cast<char *>(&Map), sizeof(StringEntry));257}258StrTab.write(OS);259// Add padding to required image alignment.260OS.write_zeros(TheEntry.ImageOffset - OS.tell());261OS << OffloadingData.Image->getBuffer();262263// Add final padding to required alignment.264assert(TheHeader.Size >= OS.tell() && "Too much data written?");265OS.write_zeros(TheHeader.Size - OS.tell());266assert(TheHeader.Size == OS.tell() && "Size mismatch");267268return Data;269}270271Error object::extractOffloadBinaries(MemoryBufferRef Buffer,272SmallVectorImpl<OffloadFile> &Binaries) {273file_magic Type = identify_magic(Buffer.getBuffer());274switch (Type) {275case file_magic::bitcode:276return extractFromBitcode(Buffer, Binaries);277case file_magic::elf_relocatable:278case file_magic::elf_executable:279case file_magic::elf_shared_object:280case file_magic::coff_object: {281Expected<std::unique_ptr<ObjectFile>> ObjFile =282ObjectFile::createObjectFile(Buffer, Type);283if (!ObjFile)284return ObjFile.takeError();285return extractFromObject(*ObjFile->get(), Binaries);286}287case file_magic::archive: {288Expected<std::unique_ptr<llvm::object::Archive>> LibFile =289object::Archive::create(Buffer);290if (!LibFile)291return LibFile.takeError();292return extractFromArchive(*LibFile->get(), Binaries);293}294case file_magic::offload_binary:295return extractOffloadFiles(Buffer, Binaries);296default:297return Error::success();298}299}300301OffloadKind object::getOffloadKind(StringRef Name) {302return llvm::StringSwitch<OffloadKind>(Name)303.Case("openmp", OFK_OpenMP)304.Case("cuda", OFK_Cuda)305.Case("hip", OFK_HIP)306.Default(OFK_None);307}308309StringRef object::getOffloadKindName(OffloadKind Kind) {310switch (Kind) {311case OFK_OpenMP:312return "openmp";313case OFK_Cuda:314return "cuda";315case OFK_HIP:316return "hip";317default:318return "none";319}320}321322ImageKind object::getImageKind(StringRef Name) {323return llvm::StringSwitch<ImageKind>(Name)324.Case("o", IMG_Object)325.Case("bc", IMG_Bitcode)326.Case("cubin", IMG_Cubin)327.Case("fatbin", IMG_Fatbinary)328.Case("s", IMG_PTX)329.Default(IMG_None);330}331332StringRef object::getImageKindName(ImageKind Kind) {333switch (Kind) {334case IMG_Object:335return "o";336case IMG_Bitcode:337return "bc";338case IMG_Cubin:339return "cubin";340case IMG_Fatbinary:341return "fatbin";342case IMG_PTX:343return "s";344default:345return "";346}347}348349bool object::areTargetsCompatible(const OffloadFile::TargetID &LHS,350const OffloadFile::TargetID &RHS) {351// Exact matches are not considered compatible because they are the same352// target. We are interested in different targets that are compatible.353if (LHS == RHS)354return false;355356// The triples must match at all times.357if (LHS.first != RHS.first)358return false;359360// If the architecture is "all" we assume it is always compatible.361if (LHS.second == "generic" || RHS.second == "generic")362return true;363364// Only The AMDGPU target requires additional checks.365llvm::Triple T(LHS.first);366if (!T.isAMDGPU())367return false;368369// The base processor must always match.370if (LHS.second.split(":").first != RHS.second.split(":").first)371return false;372373// Check combintions of on / off features that must match.374if (LHS.second.contains("xnack+") && RHS.second.contains("xnack-"))375return false;376if (LHS.second.contains("xnack-") && RHS.second.contains("xnack+"))377return false;378if (LHS.second.contains("sramecc-") && RHS.second.contains("sramecc+"))379return false;380if (LHS.second.contains("sramecc+") && RHS.second.contains("sramecc-"))381return false;382return true;383}384385386