//===- Offloading.cpp - Utilities for handling offloading code -*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Object/OffloadBinary.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/MC/StringTableBuilder.h" #include "llvm/Object/Error.h" #include "llvm/Support/Alignment.h" #include "llvm/Support/FileOutputBuffer.h" using namespace llvm; using namespace llvm::object; Expected> OffloadBinary::create(MemoryBufferRef Buf) { if (Buf.getBufferSize() < sizeof(Header) + sizeof(Entry)) return errorCodeToError(object_error::parse_failed); // Check for 0x10FF1OAD magic bytes. if (identify_magic(Buf.getBuffer()) != file_magic::offload_binary) return errorCodeToError(object_error::parse_failed); // Make sure that the data has sufficient alignment. if (!isAddrAligned(Align(getAlignment()), Buf.getBufferStart())) return errorCodeToError(object_error::parse_failed); const char *Start = Buf.getBufferStart(); const Header *TheHeader = reinterpret_cast(Start); if (TheHeader->Version != OffloadBinary::Version) return errorCodeToError(object_error::parse_failed); if (TheHeader->Size > Buf.getBufferSize() || TheHeader->EntryOffset > TheHeader->Size - sizeof(Entry) || TheHeader->EntrySize > TheHeader->Size - sizeof(Header)) return errorCodeToError(object_error::unexpected_eof); const Entry *TheEntry = reinterpret_cast(&Start[TheHeader->EntryOffset]); if (TheEntry->ImageOffset > Buf.getBufferSize() || TheEntry->StringOffset > Buf.getBufferSize()) return errorCodeToError(object_error::unexpected_eof); return std::unique_ptr( new OffloadBinary(Buf, TheHeader, TheEntry)); } std::unique_ptr OffloadBinary::write(const OffloadingImage &OffloadingData) { // Create a null-terminated string table with all the used strings. StringTableBuilder StrTab(StringTableBuilder::ELF); for (auto &KeyAndValue : OffloadingData.StringData) { StrTab.add(KeyAndValue.getKey()); StrTab.add(KeyAndValue.getValue()); } StrTab.finalize(); uint64_t StringEntrySize = sizeof(StringEntry) * OffloadingData.StringData.size(); // Make sure the image we're wrapping around is aligned as well. uint64_t BinaryDataSize = alignTo(sizeof(Header) + sizeof(Entry) + StringEntrySize + StrTab.getSize(), getAlignment()); // Create the header and fill in the offsets. The entry will be directly // placed after the header in memory. Align the size to the alignment of the // header so this can be placed contiguously in a single section. Header TheHeader; TheHeader.Size = alignTo( BinaryDataSize + OffloadingData.Image->getBufferSize(), getAlignment()); TheHeader.EntryOffset = sizeof(Header); TheHeader.EntrySize = sizeof(Entry); // Create the entry using the string table offsets. The string table will be // placed directly after the entry in memory, and the image after that. Entry TheEntry; TheEntry.TheImageKind = OffloadingData.TheImageKind; TheEntry.TheOffloadKind = OffloadingData.TheOffloadKind; TheEntry.Flags = OffloadingData.Flags; TheEntry.StringOffset = sizeof(Header) + sizeof(Entry); TheEntry.NumStrings = OffloadingData.StringData.size(); TheEntry.ImageOffset = BinaryDataSize; TheEntry.ImageSize = OffloadingData.Image->getBufferSize(); SmallVector Data; Data.reserve(TheHeader.Size); raw_svector_ostream OS(Data); OS << StringRef(reinterpret_cast(&TheHeader), sizeof(Header)); OS << StringRef(reinterpret_cast(&TheEntry), sizeof(Entry)); for (auto &KeyAndValue : OffloadingData.StringData) { uint64_t Offset = sizeof(Header) + sizeof(Entry) + StringEntrySize; StringEntry Map{Offset + StrTab.getOffset(KeyAndValue.getKey()), Offset + StrTab.getOffset(KeyAndValue.getValue())}; OS << StringRef(reinterpret_cast(&Map), sizeof(StringEntry)); } StrTab.write(OS); // Add padding to required image alignment. OS.write_zeros(TheEntry.ImageOffset - OS.tell()); OS << OffloadingData.Image->getBuffer(); // Add final padding to required alignment. assert(TheHeader.Size >= OS.tell() && "Too much data written?"); OS.write_zeros(TheHeader.Size - OS.tell()); assert(TheHeader.Size == OS.tell() && "Size mismatch"); return MemoryBuffer::getMemBufferCopy(OS.str()); } OffloadKind object::getOffloadKind(StringRef Name) { return llvm::StringSwitch(Name) .Case("openmp", OFK_OpenMP) .Case("cuda", OFK_Cuda) .Case("hip", OFK_HIP) .Default(OFK_None); } StringRef object::getOffloadKindName(OffloadKind Kind) { switch (Kind) { case OFK_OpenMP: return "openmp"; case OFK_Cuda: return "cuda"; case OFK_HIP: return "hip"; default: return "none"; } } ImageKind object::getImageKind(StringRef Name) { return llvm::StringSwitch(Name) .Case("o", IMG_Object) .Case("bc", IMG_Bitcode) .Case("cubin", IMG_Cubin) .Case("fatbin", IMG_Fatbinary) .Case("s", IMG_PTX) .Default(IMG_None); } StringRef object::getImageKindName(ImageKind Kind) { switch (Kind) { case IMG_Object: return "o"; case IMG_Bitcode: return "bc"; case IMG_Cubin: return "cubin"; case IMG_Fatbinary: return "fatbin"; case IMG_PTX: return "s"; default: return ""; } }