xref: /llvm-project/llvm/lib/Frontend/Offloading/Utility.cpp (revision 13dcc95dcd4999ff99f2de89d881f1aed5b21709)
19fa9d9a7SFabian Mora //===- Utility.cpp ------ Collection of generic offloading utilities ------===//
2078ae8cdSJoseph Huber //
3078ae8cdSJoseph Huber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4078ae8cdSJoseph Huber // See https://llvm.org/LICENSE.txt for license information.
5078ae8cdSJoseph Huber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6078ae8cdSJoseph Huber //
7078ae8cdSJoseph Huber //===----------------------------------------------------------------------===//
8078ae8cdSJoseph Huber 
9078ae8cdSJoseph Huber #include "llvm/Frontend/Offloading/Utility.h"
10cfc76b64SFabian Mora #include "llvm/BinaryFormat/AMDGPUMetadataVerifier.h"
11cfc76b64SFabian Mora #include "llvm/BinaryFormat/ELF.h"
12cfc76b64SFabian Mora #include "llvm/BinaryFormat/MsgPackDocument.h"
13078ae8cdSJoseph Huber #include "llvm/IR/Constants.h"
14078ae8cdSJoseph Huber #include "llvm/IR/GlobalValue.h"
15078ae8cdSJoseph Huber #include "llvm/IR/GlobalVariable.h"
16078ae8cdSJoseph Huber #include "llvm/IR/Value.h"
17cfc76b64SFabian Mora #include "llvm/Object/ELFObjectFile.h"
18cfc76b64SFabian Mora #include "llvm/Support/MemoryBufferRef.h"
19a551703cSJoseph Huber #include "llvm/Transforms/Utils/ModuleUtils.h"
20078ae8cdSJoseph Huber 
21078ae8cdSJoseph Huber using namespace llvm;
22078ae8cdSJoseph Huber using namespace llvm::offloading;
23078ae8cdSJoseph Huber 
249c0e6499SJoseph Huber StructType *offloading::getEntryTy(Module &M) {
25078ae8cdSJoseph Huber   LLVMContext &C = M.getContext();
26078ae8cdSJoseph Huber   StructType *EntryTy =
27078ae8cdSJoseph Huber       StructType::getTypeByName(C, "struct.__tgt_offload_entry");
28078ae8cdSJoseph Huber   if (!EntryTy)
297b9d73c2SPaulo Matos     EntryTy = StructType::create(
30*13dcc95dSJoseph Huber         "struct.__tgt_offload_entry", Type::getInt64Ty(C), Type::getInt16Ty(C),
31*13dcc95dSJoseph Huber         Type::getInt16Ty(C), Type::getInt32Ty(C), PointerType::getUnqual(C),
32*13dcc95dSJoseph Huber         PointerType::getUnqual(C), Type::getInt64Ty(C), Type::getInt64Ty(C),
33*13dcc95dSJoseph Huber         PointerType::getUnqual(C));
34078ae8cdSJoseph Huber   return EntryTy;
35078ae8cdSJoseph Huber }
36078ae8cdSJoseph Huber 
379fa9d9a7SFabian Mora std::pair<Constant *, GlobalVariable *>
38*13dcc95dSJoseph Huber offloading::getOffloadingEntryInitializer(Module &M, object::OffloadKind Kind,
39*13dcc95dSJoseph Huber                                           Constant *Addr, StringRef Name,
40*13dcc95dSJoseph Huber                                           uint64_t Size, uint32_t Flags,
41*13dcc95dSJoseph Huber                                           uint64_t Data, Constant *AuxAddr) {
423bf88163SJoseph Huber   llvm::Triple Triple(M.getTargetTriple());
43*13dcc95dSJoseph Huber   Type *PtrTy = PointerType::getUnqual(M.getContext());
44*13dcc95dSJoseph Huber   Type *Int64Ty = Type::getInt64Ty(M.getContext());
45078ae8cdSJoseph Huber   Type *Int32Ty = Type::getInt32Ty(M.getContext());
46*13dcc95dSJoseph Huber   Type *Int16Ty = Type::getInt16Ty(M.getContext());
47078ae8cdSJoseph Huber 
48078ae8cdSJoseph Huber   Constant *AddrName = ConstantDataArray::getString(M.getContext(), Name);
49078ae8cdSJoseph Huber 
50470aefb2SJoseph Huber   StringRef Prefix =
51470aefb2SJoseph Huber       Triple.isNVPTX() ? "$offloading$entry_name" : ".offloading.entry_name";
523bf88163SJoseph Huber 
53078ae8cdSJoseph Huber   // Create the constant string used to look up the symbol in the device.
543bf88163SJoseph Huber   auto *Str =
553bf88163SJoseph Huber       new GlobalVariable(M, AddrName->getType(), /*isConstant=*/true,
563bf88163SJoseph Huber                          GlobalValue::InternalLinkage, AddrName, Prefix);
5742eb54b7SJoseph Huber   StringRef SectionName = ".llvm.rodata.offloading";
5852204a29SJoseph Huber   Str->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
5942eb54b7SJoseph Huber   Str->setSection(SectionName);
6042eb54b7SJoseph Huber   Str->setAlignment(Align(1));
6142eb54b7SJoseph Huber 
6242eb54b7SJoseph Huber   // Make a metadata node for these constants so it can be queried from IR.
6342eb54b7SJoseph Huber   NamedMDNode *MD = M.getOrInsertNamedMetadata("llvm.offloading.symbols");
6442eb54b7SJoseph Huber   Metadata *MDVals[] = {ConstantAsMetadata::get(Str)};
6542eb54b7SJoseph Huber   MD->addOperand(llvm::MDNode::get(M.getContext(), MDVals));
66078ae8cdSJoseph Huber 
67078ae8cdSJoseph Huber   // Construct the offloading entry.
68078ae8cdSJoseph Huber   Constant *EntryData[] = {
69*13dcc95dSJoseph Huber       ConstantExpr::getNullValue(Int64Ty),
70*13dcc95dSJoseph Huber       ConstantInt::get(Int16Ty, 1),
71*13dcc95dSJoseph Huber       ConstantInt::get(Int16Ty, Kind),
72078ae8cdSJoseph Huber       ConstantInt::get(Int32Ty, Flags),
73*13dcc95dSJoseph Huber       ConstantExpr::getPointerBitCastOrAddrSpaceCast(Addr, PtrTy),
74*13dcc95dSJoseph Huber       ConstantExpr::getPointerBitCastOrAddrSpaceCast(Str, PtrTy),
75*13dcc95dSJoseph Huber       ConstantInt::get(Int64Ty, Size),
76*13dcc95dSJoseph Huber       ConstantInt::get(Int64Ty, Data),
77*13dcc95dSJoseph Huber       AuxAddr ? ConstantExpr::getPointerBitCastOrAddrSpaceCast(AuxAddr, PtrTy)
78*13dcc95dSJoseph Huber               : ConstantExpr::getNullValue(PtrTy)};
79078ae8cdSJoseph Huber   Constant *EntryInitializer = ConstantStruct::get(getEntryTy(M), EntryData);
809fa9d9a7SFabian Mora   return {EntryInitializer, Str};
819fa9d9a7SFabian Mora }
829fa9d9a7SFabian Mora 
83*13dcc95dSJoseph Huber void offloading::emitOffloadingEntry(Module &M, object::OffloadKind Kind,
84*13dcc95dSJoseph Huber                                      Constant *Addr, StringRef Name,
85*13dcc95dSJoseph Huber                                      uint64_t Size, uint32_t Flags,
86*13dcc95dSJoseph Huber                                      uint64_t Data, StringRef SectionName,
87*13dcc95dSJoseph Huber                                      Constant *AuxAddr) {
889fa9d9a7SFabian Mora   llvm::Triple Triple(M.getTargetTriple());
899fa9d9a7SFabian Mora 
90*13dcc95dSJoseph Huber   auto [EntryInitializer, NameGV] = getOffloadingEntryInitializer(
91*13dcc95dSJoseph Huber       M, Kind, Addr, Name, Size, Flags, Data, AuxAddr);
92078ae8cdSJoseph Huber 
933bf88163SJoseph Huber   StringRef Prefix =
94470aefb2SJoseph Huber       Triple.isNVPTX() ? "$offloading$entry$" : ".offloading.entry.";
95078ae8cdSJoseph Huber   auto *Entry = new GlobalVariable(
96078ae8cdSJoseph Huber       M, getEntryTy(M),
97078ae8cdSJoseph Huber       /*isConstant=*/true, GlobalValue::WeakAnyLinkage, EntryInitializer,
983bf88163SJoseph Huber       Prefix + Name, nullptr, GlobalValue::NotThreadLocal,
99078ae8cdSJoseph Huber       M.getDataLayout().getDefaultGlobalsAddressSpace());
100078ae8cdSJoseph Huber 
101078ae8cdSJoseph Huber   // The entry has to be created in the section the linker expects it to be.
10252204a29SJoseph Huber   if (Triple.isOSBinFormatCOFF())
10352204a29SJoseph Huber     Entry->setSection((SectionName + "$OE").str());
10452204a29SJoseph Huber   else
105078ae8cdSJoseph Huber     Entry->setSection(SectionName);
106078ae8cdSJoseph Huber   Entry->setAlignment(Align(1));
107078ae8cdSJoseph Huber }
1089c0e6499SJoseph Huber 
1099c0e6499SJoseph Huber std::pair<GlobalVariable *, GlobalVariable *>
1109c0e6499SJoseph Huber offloading::getOffloadEntryArray(Module &M, StringRef SectionName) {
11152204a29SJoseph Huber   llvm::Triple Triple(M.getTargetTriple());
11252204a29SJoseph Huber 
11352204a29SJoseph Huber   auto *ZeroInitilaizer =
11452204a29SJoseph Huber       ConstantAggregateZero::get(ArrayType::get(getEntryTy(M), 0u));
11552204a29SJoseph Huber   auto *EntryInit = Triple.isOSBinFormatCOFF() ? ZeroInitilaizer : nullptr;
11652204a29SJoseph Huber   auto *EntryType = ArrayType::get(getEntryTy(M), 0);
117a551703cSJoseph Huber   auto Linkage = Triple.isOSBinFormatCOFF() ? GlobalValue::WeakODRLinkage
118a551703cSJoseph Huber                                             : GlobalValue::ExternalLinkage;
11952204a29SJoseph Huber 
120a551703cSJoseph Huber   auto *EntriesB =
121a551703cSJoseph Huber       new GlobalVariable(M, EntryType, /*isConstant=*/true, Linkage, EntryInit,
12252204a29SJoseph Huber                          "__start_" + SectionName);
1239c0e6499SJoseph Huber   EntriesB->setVisibility(GlobalValue::HiddenVisibility);
124a551703cSJoseph Huber   auto *EntriesE =
125a551703cSJoseph Huber       new GlobalVariable(M, EntryType, /*isConstant=*/true, Linkage, EntryInit,
12652204a29SJoseph Huber                          "__stop_" + SectionName);
1279c0e6499SJoseph Huber   EntriesE->setVisibility(GlobalValue::HiddenVisibility);
1289c0e6499SJoseph Huber 
12952204a29SJoseph Huber   if (Triple.isOSBinFormatELF()) {
1309c0e6499SJoseph Huber     // We assume that external begin/end symbols that we have created above will
13152204a29SJoseph Huber     // be defined by the linker. This is done whenever a section name with a
13252204a29SJoseph Huber     // valid C-identifier is present. We define a dummy variable here to force
13352204a29SJoseph Huber     // the linker to always provide these symbols.
13452204a29SJoseph Huber     auto *DummyEntry = new GlobalVariable(
135a551703cSJoseph Huber         M, ZeroInitilaizer->getType(), true, GlobalVariable::InternalLinkage,
13652204a29SJoseph Huber         ZeroInitilaizer, "__dummy." + SectionName);
1379c0e6499SJoseph Huber     DummyEntry->setSection(SectionName);
138a551703cSJoseph Huber     appendToCompilerUsed(M, DummyEntry);
13952204a29SJoseph Huber   } else {
14052204a29SJoseph Huber     // The COFF linker will merge sections containing a '$' together into a
14152204a29SJoseph Huber     // single section. The order of entries in this section will be sorted
14252204a29SJoseph Huber     // alphabetically by the characters following the '$' in the name. Set the
14352204a29SJoseph Huber     // sections here to ensure that the beginning and end symbols are sorted.
14452204a29SJoseph Huber     EntriesB->setSection((SectionName + "$OA").str());
14552204a29SJoseph Huber     EntriesE->setSection((SectionName + "$OZ").str());
14652204a29SJoseph Huber   }
1479c0e6499SJoseph Huber 
1489c0e6499SJoseph Huber   return std::make_pair(EntriesB, EntriesE);
1499c0e6499SJoseph Huber }
150cfc76b64SFabian Mora 
151cfc76b64SFabian Mora bool llvm::offloading::amdgpu::isImageCompatibleWithEnv(StringRef ImageArch,
152cfc76b64SFabian Mora                                                         uint32_t ImageFlags,
153cfc76b64SFabian Mora                                                         StringRef EnvTargetID) {
154cfc76b64SFabian Mora   using namespace llvm::ELF;
155cfc76b64SFabian Mora   StringRef EnvArch = EnvTargetID.split(":").first;
156cfc76b64SFabian Mora 
157cfc76b64SFabian Mora   // Trivial check if the base processors match.
158cfc76b64SFabian Mora   if (EnvArch != ImageArch)
159cfc76b64SFabian Mora     return false;
160cfc76b64SFabian Mora 
161cfc76b64SFabian Mora   // Check if the image is requesting xnack on or off.
162cfc76b64SFabian Mora   switch (ImageFlags & EF_AMDGPU_FEATURE_XNACK_V4) {
163cfc76b64SFabian Mora   case EF_AMDGPU_FEATURE_XNACK_OFF_V4:
164cfc76b64SFabian Mora     // The image is 'xnack-' so the environment must be 'xnack-'.
165cfc76b64SFabian Mora     if (!EnvTargetID.contains("xnack-"))
166cfc76b64SFabian Mora       return false;
167cfc76b64SFabian Mora     break;
168cfc76b64SFabian Mora   case EF_AMDGPU_FEATURE_XNACK_ON_V4:
169cfc76b64SFabian Mora     // The image is 'xnack+' so the environment must be 'xnack+'.
170cfc76b64SFabian Mora     if (!EnvTargetID.contains("xnack+"))
171cfc76b64SFabian Mora       return false;
172cfc76b64SFabian Mora     break;
173cfc76b64SFabian Mora   case EF_AMDGPU_FEATURE_XNACK_UNSUPPORTED_V4:
174cfc76b64SFabian Mora   case EF_AMDGPU_FEATURE_XNACK_ANY_V4:
175cfc76b64SFabian Mora   default:
176cfc76b64SFabian Mora     break;
177cfc76b64SFabian Mora   }
178cfc76b64SFabian Mora 
179cfc76b64SFabian Mora   // Check if the image is requesting sramecc on or off.
180cfc76b64SFabian Mora   switch (ImageFlags & EF_AMDGPU_FEATURE_SRAMECC_V4) {
181cfc76b64SFabian Mora   case EF_AMDGPU_FEATURE_SRAMECC_OFF_V4:
182cfc76b64SFabian Mora     // The image is 'sramecc-' so the environment must be 'sramecc-'.
183cfc76b64SFabian Mora     if (!EnvTargetID.contains("sramecc-"))
184cfc76b64SFabian Mora       return false;
185cfc76b64SFabian Mora     break;
186cfc76b64SFabian Mora   case EF_AMDGPU_FEATURE_SRAMECC_ON_V4:
187cfc76b64SFabian Mora     // The image is 'sramecc+' so the environment must be 'sramecc+'.
188cfc76b64SFabian Mora     if (!EnvTargetID.contains("sramecc+"))
189cfc76b64SFabian Mora       return false;
190cfc76b64SFabian Mora     break;
191cfc76b64SFabian Mora   case EF_AMDGPU_FEATURE_SRAMECC_UNSUPPORTED_V4:
192cfc76b64SFabian Mora   case EF_AMDGPU_FEATURE_SRAMECC_ANY_V4:
193cfc76b64SFabian Mora     break;
194cfc76b64SFabian Mora   }
195cfc76b64SFabian Mora 
196cfc76b64SFabian Mora   return true;
197cfc76b64SFabian Mora }
198cfc76b64SFabian Mora 
199cfc76b64SFabian Mora namespace {
200cfc76b64SFabian Mora /// Reads the AMDGPU specific per-kernel-metadata from an image.
201cfc76b64SFabian Mora class KernelInfoReader {
202cfc76b64SFabian Mora public:
203cfc76b64SFabian Mora   KernelInfoReader(StringMap<offloading::amdgpu::AMDGPUKernelMetaData> &KIM)
204cfc76b64SFabian Mora       : KernelInfoMap(KIM) {}
205cfc76b64SFabian Mora 
206cfc76b64SFabian Mora   /// Process ELF note to read AMDGPU metadata from respective information
207cfc76b64SFabian Mora   /// fields.
208cfc76b64SFabian Mora   Error processNote(const llvm::object::ELF64LE::Note &Note, size_t Align) {
209cfc76b64SFabian Mora     if (Note.getName() != "AMDGPU")
210cfc76b64SFabian Mora       return Error::success(); // We are not interested in other things
211cfc76b64SFabian Mora 
212cfc76b64SFabian Mora     assert(Note.getType() == ELF::NT_AMDGPU_METADATA &&
213cfc76b64SFabian Mora            "Parse AMDGPU MetaData");
214cfc76b64SFabian Mora     auto Desc = Note.getDesc(Align);
215cfc76b64SFabian Mora     StringRef MsgPackString =
216cfc76b64SFabian Mora         StringRef(reinterpret_cast<const char *>(Desc.data()), Desc.size());
217cfc76b64SFabian Mora     msgpack::Document MsgPackDoc;
218cfc76b64SFabian Mora     if (!MsgPackDoc.readFromBlob(MsgPackString, /*Multi=*/false))
219cfc76b64SFabian Mora       return Error::success();
220cfc76b64SFabian Mora 
221cfc76b64SFabian Mora     AMDGPU::HSAMD::V3::MetadataVerifier Verifier(true);
222cfc76b64SFabian Mora     if (!Verifier.verify(MsgPackDoc.getRoot()))
223cfc76b64SFabian Mora       return Error::success();
224cfc76b64SFabian Mora 
225cfc76b64SFabian Mora     auto RootMap = MsgPackDoc.getRoot().getMap(true);
226cfc76b64SFabian Mora 
227cfc76b64SFabian Mora     if (auto Err = iterateAMDKernels(RootMap))
228cfc76b64SFabian Mora       return Err;
229cfc76b64SFabian Mora 
230cfc76b64SFabian Mora     return Error::success();
231cfc76b64SFabian Mora   }
232cfc76b64SFabian Mora 
233cfc76b64SFabian Mora private:
234cfc76b64SFabian Mora   /// Extracts the relevant information via simple string look-up in the msgpack
235cfc76b64SFabian Mora   /// document elements.
236cfc76b64SFabian Mora   Error
237cfc76b64SFabian Mora   extractKernelData(msgpack::MapDocNode::MapTy::value_type V,
238cfc76b64SFabian Mora                     std::string &KernelName,
239cfc76b64SFabian Mora                     offloading::amdgpu::AMDGPUKernelMetaData &KernelData) {
240cfc76b64SFabian Mora     if (!V.first.isString())
241cfc76b64SFabian Mora       return Error::success();
242cfc76b64SFabian Mora 
243cfc76b64SFabian Mora     const auto IsKey = [](const msgpack::DocNode &DK, StringRef SK) {
244cfc76b64SFabian Mora       return DK.getString() == SK;
245cfc76b64SFabian Mora     };
246cfc76b64SFabian Mora 
247cfc76b64SFabian Mora     const auto GetSequenceOfThreeInts = [](msgpack::DocNode &DN,
248cfc76b64SFabian Mora                                            uint32_t *Vals) {
249cfc76b64SFabian Mora       assert(DN.isArray() && "MsgPack DocNode is an array node");
250cfc76b64SFabian Mora       auto DNA = DN.getArray();
251cfc76b64SFabian Mora       assert(DNA.size() == 3 && "ArrayNode has at most three elements");
252cfc76b64SFabian Mora 
253cfc76b64SFabian Mora       int I = 0;
254cfc76b64SFabian Mora       for (auto DNABegin = DNA.begin(), DNAEnd = DNA.end(); DNABegin != DNAEnd;
255cfc76b64SFabian Mora            ++DNABegin) {
256cfc76b64SFabian Mora         Vals[I++] = DNABegin->getUInt();
257cfc76b64SFabian Mora       }
258cfc76b64SFabian Mora     };
259cfc76b64SFabian Mora 
260cfc76b64SFabian Mora     if (IsKey(V.first, ".name")) {
261cfc76b64SFabian Mora       KernelName = V.second.toString();
262cfc76b64SFabian Mora     } else if (IsKey(V.first, ".sgpr_count")) {
263cfc76b64SFabian Mora       KernelData.SGPRCount = V.second.getUInt();
264cfc76b64SFabian Mora     } else if (IsKey(V.first, ".sgpr_spill_count")) {
265cfc76b64SFabian Mora       KernelData.SGPRSpillCount = V.second.getUInt();
266cfc76b64SFabian Mora     } else if (IsKey(V.first, ".vgpr_count")) {
267cfc76b64SFabian Mora       KernelData.VGPRCount = V.second.getUInt();
268cfc76b64SFabian Mora     } else if (IsKey(V.first, ".vgpr_spill_count")) {
269cfc76b64SFabian Mora       KernelData.VGPRSpillCount = V.second.getUInt();
270cfc76b64SFabian Mora     } else if (IsKey(V.first, ".agpr_count")) {
271cfc76b64SFabian Mora       KernelData.AGPRCount = V.second.getUInt();
272cfc76b64SFabian Mora     } else if (IsKey(V.first, ".private_segment_fixed_size")) {
273cfc76b64SFabian Mora       KernelData.PrivateSegmentSize = V.second.getUInt();
274cfc76b64SFabian Mora     } else if (IsKey(V.first, ".group_segment_fixed_size")) {
275cfc76b64SFabian Mora       KernelData.GroupSegmentList = V.second.getUInt();
276cfc76b64SFabian Mora     } else if (IsKey(V.first, ".reqd_workgroup_size")) {
277cfc76b64SFabian Mora       GetSequenceOfThreeInts(V.second, KernelData.RequestedWorkgroupSize);
278cfc76b64SFabian Mora     } else if (IsKey(V.first, ".workgroup_size_hint")) {
279cfc76b64SFabian Mora       GetSequenceOfThreeInts(V.second, KernelData.WorkgroupSizeHint);
280cfc76b64SFabian Mora     } else if (IsKey(V.first, ".wavefront_size")) {
281cfc76b64SFabian Mora       KernelData.WavefrontSize = V.second.getUInt();
282cfc76b64SFabian Mora     } else if (IsKey(V.first, ".max_flat_workgroup_size")) {
283cfc76b64SFabian Mora       KernelData.MaxFlatWorkgroupSize = V.second.getUInt();
284cfc76b64SFabian Mora     }
285cfc76b64SFabian Mora 
286cfc76b64SFabian Mora     return Error::success();
287cfc76b64SFabian Mora   }
288cfc76b64SFabian Mora 
289cfc76b64SFabian Mora   /// Get the "amdhsa.kernels" element from the msgpack Document
290cfc76b64SFabian Mora   Expected<msgpack::ArrayDocNode> getAMDKernelsArray(msgpack::MapDocNode &MDN) {
291cfc76b64SFabian Mora     auto Res = MDN.find("amdhsa.kernels");
292cfc76b64SFabian Mora     if (Res == MDN.end())
293cfc76b64SFabian Mora       return createStringError(inconvertibleErrorCode(),
294cfc76b64SFabian Mora                                "Could not find amdhsa.kernels key");
295cfc76b64SFabian Mora 
296cfc76b64SFabian Mora     auto Pair = *Res;
297cfc76b64SFabian Mora     assert(Pair.second.isArray() &&
298cfc76b64SFabian Mora            "AMDGPU kernel entries are arrays of entries");
299cfc76b64SFabian Mora 
300cfc76b64SFabian Mora     return Pair.second.getArray();
301cfc76b64SFabian Mora   }
302cfc76b64SFabian Mora 
303cfc76b64SFabian Mora   /// Iterate all entries for one "amdhsa.kernels" entry. Each entry is a
304cfc76b64SFabian Mora   /// MapDocNode that either maps a string to a single value (most of them) or
305cfc76b64SFabian Mora   /// to another array of things. Currently, we only handle the case that maps
306cfc76b64SFabian Mora   /// to scalar value.
307cfc76b64SFabian Mora   Error generateKernelInfo(msgpack::ArrayDocNode::ArrayTy::iterator It) {
308cfc76b64SFabian Mora     offloading::amdgpu::AMDGPUKernelMetaData KernelData;
309cfc76b64SFabian Mora     std::string KernelName;
310cfc76b64SFabian Mora     auto Entry = (*It).getMap();
311cfc76b64SFabian Mora     for (auto MI = Entry.begin(), E = Entry.end(); MI != E; ++MI)
312cfc76b64SFabian Mora       if (auto Err = extractKernelData(*MI, KernelName, KernelData))
313cfc76b64SFabian Mora         return Err;
314cfc76b64SFabian Mora 
315cfc76b64SFabian Mora     KernelInfoMap.insert({KernelName, KernelData});
316cfc76b64SFabian Mora     return Error::success();
317cfc76b64SFabian Mora   }
318cfc76b64SFabian Mora 
319cfc76b64SFabian Mora   /// Go over the list of AMD kernels in the "amdhsa.kernels" entry
320cfc76b64SFabian Mora   Error iterateAMDKernels(msgpack::MapDocNode &MDN) {
321cfc76b64SFabian Mora     auto KernelsOrErr = getAMDKernelsArray(MDN);
322cfc76b64SFabian Mora     if (auto Err = KernelsOrErr.takeError())
323cfc76b64SFabian Mora       return Err;
324cfc76b64SFabian Mora 
325cfc76b64SFabian Mora     auto KernelsArr = *KernelsOrErr;
326cfc76b64SFabian Mora     for (auto It = KernelsArr.begin(), E = KernelsArr.end(); It != E; ++It) {
327cfc76b64SFabian Mora       if (!It->isMap())
328cfc76b64SFabian Mora         continue; // we expect <key,value> pairs
329cfc76b64SFabian Mora 
330cfc76b64SFabian Mora       // Obtain the value for the different entries. Each array entry is a
331cfc76b64SFabian Mora       // MapDocNode
332cfc76b64SFabian Mora       if (auto Err = generateKernelInfo(It))
333cfc76b64SFabian Mora         return Err;
334cfc76b64SFabian Mora     }
335cfc76b64SFabian Mora     return Error::success();
336cfc76b64SFabian Mora   }
337cfc76b64SFabian Mora 
338cfc76b64SFabian Mora   // Kernel names are the keys
339cfc76b64SFabian Mora   StringMap<offloading::amdgpu::AMDGPUKernelMetaData> &KernelInfoMap;
340cfc76b64SFabian Mora };
341cfc76b64SFabian Mora } // namespace
342cfc76b64SFabian Mora 
343cfc76b64SFabian Mora Error llvm::offloading::amdgpu::getAMDGPUMetaDataFromImage(
344cfc76b64SFabian Mora     MemoryBufferRef MemBuffer,
345cfc76b64SFabian Mora     StringMap<offloading::amdgpu::AMDGPUKernelMetaData> &KernelInfoMap,
346cfc76b64SFabian Mora     uint16_t &ELFABIVersion) {
347cfc76b64SFabian Mora   Error Err = Error::success(); // Used later as out-parameter
348cfc76b64SFabian Mora 
349cfc76b64SFabian Mora   auto ELFOrError = object::ELF64LEFile::create(MemBuffer.getBuffer());
350cfc76b64SFabian Mora   if (auto Err = ELFOrError.takeError())
351cfc76b64SFabian Mora     return Err;
352cfc76b64SFabian Mora 
353cfc76b64SFabian Mora   const object::ELF64LEFile ELFObj = ELFOrError.get();
354cfc76b64SFabian Mora   Expected<ArrayRef<object::ELF64LE::Shdr>> Sections = ELFObj.sections();
355cfc76b64SFabian Mora   if (!Sections)
356cfc76b64SFabian Mora     return Sections.takeError();
357cfc76b64SFabian Mora   KernelInfoReader Reader(KernelInfoMap);
358cfc76b64SFabian Mora 
359cfc76b64SFabian Mora   // Read the code object version from ELF image header
360cfc76b64SFabian Mora   auto Header = ELFObj.getHeader();
361cfc76b64SFabian Mora   ELFABIVersion = (uint8_t)(Header.e_ident[ELF::EI_ABIVERSION]);
362cfc76b64SFabian Mora   for (const auto &S : *Sections) {
363cfc76b64SFabian Mora     if (S.sh_type != ELF::SHT_NOTE)
364cfc76b64SFabian Mora       continue;
365cfc76b64SFabian Mora 
366cfc76b64SFabian Mora     for (const auto N : ELFObj.notes(S, Err)) {
367cfc76b64SFabian Mora       if (Err)
368cfc76b64SFabian Mora         return Err;
369cfc76b64SFabian Mora       // Fills the KernelInfoTabel entries in the reader
370cfc76b64SFabian Mora       if ((Err = Reader.processNote(N, S.sh_addralign)))
371cfc76b64SFabian Mora         return Err;
372cfc76b64SFabian Mora     }
373cfc76b64SFabian Mora   }
374cfc76b64SFabian Mora   return Error::success();
375cfc76b64SFabian Mora }
376