165 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			165 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- 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<std::unique_ptr<OffloadBinary>>
 | |
| 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<const Header *>(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<const Entry *>(&Start[TheHeader->EntryOffset]);
 | |
| 
 | |
|   if (TheEntry->ImageOffset > Buf.getBufferSize() ||
 | |
|       TheEntry->StringOffset > Buf.getBufferSize())
 | |
|     return errorCodeToError(object_error::unexpected_eof);
 | |
| 
 | |
|   return std::unique_ptr<OffloadBinary>(
 | |
|       new OffloadBinary(Buf, TheHeader, TheEntry));
 | |
| }
 | |
| 
 | |
| std::unique_ptr<MemoryBuffer>
 | |
| 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<char> Data;
 | |
|   Data.reserve(TheHeader.Size);
 | |
|   raw_svector_ostream OS(Data);
 | |
|   OS << StringRef(reinterpret_cast<char *>(&TheHeader), sizeof(Header));
 | |
|   OS << StringRef(reinterpret_cast<char *>(&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<char *>(&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<OffloadKind>(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<ImageKind>(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 "";
 | |
|   }
 | |
| }
 |