1 //===- Offloading.cpp - Utilities for handling offloading code  -*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "llvm/Object/OffloadBinary.h"
10 
11 #include "llvm/ADT/StringSwitch.h"
12 #include "llvm/BinaryFormat/Magic.h"
13 #include "llvm/MC/StringTableBuilder.h"
14 #include "llvm/Object/Error.h"
15 #include "llvm/Support/FileOutputBuffer.h"
16 
17 namespace llvm {
18 
19 namespace object {
20 
21 Expected<std::unique_ptr<OffloadBinary>>
22 OffloadBinary::create(MemoryBufferRef Buf) {
23   if (Buf.getBufferSize() < sizeof(Header) + sizeof(Entry))
24     return errorCodeToError(object_error::parse_failed);
25 
26   // Check for 0x10FF1OAD magic bytes.
27   if (identify_magic(Buf.getBuffer()) != file_magic::offload_binary)
28     return errorCodeToError(object_error::parse_failed);
29 
30   const char *Start = Buf.getBufferStart();
31   const Header *TheHeader = reinterpret_cast<const Header *>(Start);
32   const Entry *TheEntry =
33       reinterpret_cast<const Entry *>(&Start[TheHeader->EntryOffset]);
34 
35   return std::unique_ptr<OffloadBinary>(
36       new OffloadBinary(Buf, TheHeader, TheEntry));
37 }
38 
39 std::unique_ptr<MemoryBuffer>
40 OffloadBinary::write(const OffloadingImage &OffloadingData) {
41   // Create a null-terminated string table with all the used strings.
42   StringTableBuilder StrTab(StringTableBuilder::ELF);
43   for (auto &KeyAndValue : OffloadingData.StringData) {
44     StrTab.add(KeyAndValue.getKey());
45     StrTab.add(KeyAndValue.getValue());
46   }
47   StrTab.finalize();
48 
49   uint64_t StringEntrySize =
50       sizeof(StringEntry) * OffloadingData.StringData.size();
51 
52   // Create the header and fill in the offsets. The entry will be directly
53   // placed after the header in memory. Align the size to the alignment of the
54   // header so this can be placed contiguously in a single section.
55   Header TheHeader;
56   TheHeader.Size =
57       alignTo(sizeof(Header) + sizeof(Entry) + StringEntrySize +
58                   OffloadingData.Image.getBufferSize() + StrTab.getSize(),
59               getAlignment());
60   TheHeader.EntryOffset = sizeof(Header);
61   TheHeader.EntrySize = sizeof(Entry);
62 
63   // Create the entry using the string table offsets. The string table will be
64   // placed directly after the entry in memory, and the image after that.
65   Entry TheEntry;
66   TheEntry.TheImageKind = OffloadingData.TheImageKind;
67   TheEntry.TheOffloadKind = OffloadingData.TheOffloadKind;
68   TheEntry.Flags = OffloadingData.Flags;
69   TheEntry.StringOffset = sizeof(Header) + sizeof(Entry);
70   TheEntry.NumStrings = OffloadingData.StringData.size();
71 
72   TheEntry.ImageOffset =
73       sizeof(Header) + sizeof(Entry) + StringEntrySize + StrTab.getSize();
74   TheEntry.ImageSize = OffloadingData.Image.getBufferSize();
75 
76   SmallVector<char, 1024> Data;
77   raw_svector_ostream OS(Data);
78   OS << StringRef(reinterpret_cast<char *>(&TheHeader), sizeof(Header));
79   OS << StringRef(reinterpret_cast<char *>(&TheEntry), sizeof(Entry));
80   for (auto &KeyAndValue : OffloadingData.StringData) {
81     uint64_t Offset = sizeof(Header) + sizeof(Entry) + StringEntrySize;
82     StringEntry Map{Offset + StrTab.getOffset(KeyAndValue.getKey()),
83                     Offset + StrTab.getOffset(KeyAndValue.getValue())};
84     OS << StringRef(reinterpret_cast<char *>(&Map), sizeof(StringEntry));
85   }
86   StrTab.write(OS);
87   OS << OffloadingData.Image.getBuffer();
88 
89   // Add final padding to required alignment.
90   assert(TheHeader.Size >= OS.tell() && "Too much data written?");
91   OS.write_zeros(TheHeader.Size - OS.tell());
92   assert(TheHeader.Size == OS.tell() && "Size mismatch");
93 
94   return MemoryBuffer::getMemBufferCopy(OS.str());
95 }
96 
97 OffloadKind getOffloadKind(StringRef Name) {
98   return llvm::StringSwitch<OffloadKind>(Name)
99       .Case("openmp", OFK_OpenMP)
100       .Case("cuda", OFK_Cuda)
101       .Case("hip", OFK_HIP)
102       .Default(OFK_None);
103 }
104 
105 StringRef getOffloadKindName(OffloadKind Kind) {
106   switch (Kind) {
107   case OFK_OpenMP:
108     return "openmp";
109   case OFK_Cuda:
110     return "cuda";
111   case OFK_HIP:
112     return "hip";
113   default:
114     return "none";
115   }
116 }
117 
118 ImageKind getImageKind(StringRef Name) {
119   return llvm::StringSwitch<ImageKind>(Name)
120       .Case("o", IMG_Object)
121       .Case("bc", IMG_Bitcode)
122       .Case("cubin", IMG_Cubin)
123       .Case("fatbin", IMG_Fatbinary)
124       .Case("s", IMG_PTX)
125       .Default(IMG_None);
126 }
127 
128 StringRef getImageKindName(ImageKind Kind) {
129   switch (Kind) {
130   case IMG_Object:
131     return "o";
132   case IMG_Bitcode:
133     return "bc";
134   case IMG_Cubin:
135     return "cubin";
136   case IMG_Fatbinary:
137     return "fatbin";
138   case IMG_PTX:
139     return "s";
140   default:
141     return "";
142   }
143 }
144 
145 } // namespace object
146 
147 } // namespace llvm
148