xref: /freebsd/contrib/llvm-project/llvm/lib/CGData/StableFunctionMapRecord.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- StableFunctionMapRecord.cpp ---------------------------------------===//
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 // This implements the functionality for the StableFunctionMapRecord class,
10 // including methods for serialization and deserialization of stable function
11 // maps to and from raw and YAML streams. It also includes utilities for
12 // managing function entries and their metadata.
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #include "llvm/CGData/StableFunctionMapRecord.h"
17 #include "llvm/Support/EndianStream.h"
18 
19 #define DEBUG_TYPE "stable-function-map-record"
20 
21 using namespace llvm;
22 using namespace llvm::support;
23 
24 LLVM_YAML_IS_SEQUENCE_VECTOR(IndexPairHash)
25 LLVM_YAML_IS_SEQUENCE_VECTOR(StableFunction)
26 
27 namespace llvm {
28 namespace yaml {
29 
30 template <> struct MappingTraits<IndexPairHash> {
mappingllvm::yaml::MappingTraits31   static void mapping(IO &IO, IndexPairHash &Key) {
32     IO.mapRequired("InstIndex", Key.first.first);
33     IO.mapRequired("OpndIndex", Key.first.second);
34     IO.mapRequired("OpndHash", Key.second);
35   }
36 };
37 
38 template <> struct MappingTraits<StableFunction> {
mappingllvm::yaml::MappingTraits39   static void mapping(IO &IO, StableFunction &Func) {
40     IO.mapRequired("Hash", Func.Hash);
41     IO.mapRequired("FunctionName", Func.FunctionName);
42     IO.mapRequired("ModuleName", Func.ModuleName);
43     IO.mapRequired("InstCount", Func.InstCount);
44     IO.mapRequired("IndexOperandHashes", Func.IndexOperandHashes);
45   }
46 };
47 
48 } // namespace yaml
49 } // namespace llvm
50 
51 // Get a sorted vector of StableFunctionEntry pointers.
52 static SmallVector<const StableFunctionMap::StableFunctionEntry *>
getStableFunctionEntries(const StableFunctionMap & SFM)53 getStableFunctionEntries(const StableFunctionMap &SFM) {
54   SmallVector<const StableFunctionMap::StableFunctionEntry *> FuncEntries;
55   for (const auto &P : SFM.getFunctionMap())
56     for (auto &Func : P.second)
57       FuncEntries.emplace_back(Func.get());
58 
59   llvm::stable_sort(
60       FuncEntries, [&](auto &A, auto &B) {
61         return std::tuple(A->Hash, SFM.getNameForId(A->ModuleNameId),
62                           SFM.getNameForId(A->FunctionNameId)) <
63                std::tuple(B->Hash, SFM.getNameForId(B->ModuleNameId),
64                           SFM.getNameForId(B->FunctionNameId));
65       });
66   return FuncEntries;
67 }
68 
69 // Get a sorted vector of IndexOperandHashes.
getStableIndexOperandHashes(const StableFunctionMap::StableFunctionEntry * FuncEntry)70 static IndexOperandHashVecType getStableIndexOperandHashes(
71     const StableFunctionMap::StableFunctionEntry *FuncEntry) {
72   IndexOperandHashVecType IndexOperandHashes;
73   for (auto &[Indices, OpndHash] : *FuncEntry->IndexOperandHashMap)
74     IndexOperandHashes.emplace_back(Indices, OpndHash);
75   // The indices are unique, so we can just sort by the first.
76   llvm::sort(IndexOperandHashes);
77   return IndexOperandHashes;
78 }
79 
serialize(raw_ostream & OS,std::vector<CGDataPatchItem> & PatchItems) const80 void StableFunctionMapRecord::serialize(
81     raw_ostream &OS, std::vector<CGDataPatchItem> &PatchItems) const {
82   serialize(OS, FunctionMap.get(), PatchItems);
83 }
84 
serialize(raw_ostream & OS,const StableFunctionMap * FunctionMap,std::vector<CGDataPatchItem> & PatchItems)85 void StableFunctionMapRecord::serialize(
86     raw_ostream &OS, const StableFunctionMap *FunctionMap,
87     std::vector<CGDataPatchItem> &PatchItems) {
88   support::endian::Writer Writer(OS, endianness::little);
89 
90   // Write Names.
91   ArrayRef<std::string> Names = FunctionMap->getNames();
92   Writer.write<uint32_t>(Names.size());
93   // Remember the position, write back the total size of Names, so we can skip
94   // reading them if needed.
95   const uint64_t NamesByteSizeOffset = Writer.OS.tell();
96   Writer.write<uint64_t>(0);
97   for (auto &Name : Names)
98     Writer.OS << Name << '\0';
99   // Align current position to 4 bytes.
100   uint32_t Padding = offsetToAlignment(Writer.OS.tell(), Align(4));
101   for (uint32_t I = 0; I < Padding; ++I)
102     Writer.OS << '\0';
103   const auto NamesByteSize =
104       Writer.OS.tell() - NamesByteSizeOffset - sizeof(NamesByteSizeOffset);
105   PatchItems.emplace_back(NamesByteSizeOffset, &NamesByteSize, 1);
106 
107   // Write StableFunctionEntries whose pointers are sorted.
108   auto FuncEntries = getStableFunctionEntries(*FunctionMap);
109   Writer.write<uint32_t>(FuncEntries.size());
110 
111   for (const auto *FuncRef : FuncEntries) {
112     Writer.write<stable_hash>(FuncRef->Hash);
113     Writer.write<uint32_t>(FuncRef->FunctionNameId);
114     Writer.write<uint32_t>(FuncRef->ModuleNameId);
115     Writer.write<uint32_t>(FuncRef->InstCount);
116 
117     // Emit IndexOperandHashes sorted from IndexOperandHashMap.
118     IndexOperandHashVecType IndexOperandHashes =
119         getStableIndexOperandHashes(FuncRef);
120     Writer.write<uint32_t>(IndexOperandHashes.size());
121     for (auto &IndexOperandHash : IndexOperandHashes) {
122       Writer.write<uint32_t>(IndexOperandHash.first.first);
123       Writer.write<uint32_t>(IndexOperandHash.first.second);
124       Writer.write<stable_hash>(IndexOperandHash.second);
125     }
126   }
127 }
128 
deserialize(const unsigned char * & Ptr,bool ReadStableFunctionMapNames)129 void StableFunctionMapRecord::deserialize(const unsigned char *&Ptr,
130                                           bool ReadStableFunctionMapNames) {
131   // Assert that Ptr is 4-byte aligned
132   assert(((uintptr_t)Ptr % 4) == 0);
133   // Read Names.
134   auto NumNames =
135       endian::readNext<uint32_t, endianness::little, unaligned>(Ptr);
136   // Early exit if there is no name.
137   if (NumNames == 0)
138     return;
139   const auto NamesByteSize =
140       endian::readNext<uint64_t, endianness::little, unaligned>(Ptr);
141   const auto NamesOffset = reinterpret_cast<uintptr_t>(Ptr);
142   if (ReadStableFunctionMapNames) {
143     for (unsigned I = 0; I < NumNames; ++I) {
144       StringRef Name(reinterpret_cast<const char *>(Ptr));
145       Ptr += Name.size() + 1;
146       FunctionMap->getIdOrCreateForName(Name);
147     }
148     // Align Ptr to 4 bytes.
149     Ptr = reinterpret_cast<const uint8_t *>(alignAddr(Ptr, Align(4)));
150     assert(reinterpret_cast<uintptr_t>(Ptr) - NamesOffset == NamesByteSize &&
151            "NamesByteSize does not match the actual size of names");
152   } else {
153     // skip reading Names by advancing the pointer.
154     Ptr = reinterpret_cast<const uint8_t *>(NamesOffset + NamesByteSize);
155   }
156 
157   // Read StableFunctionEntries.
158   auto NumFuncs =
159       endian::readNext<uint32_t, endianness::little, unaligned>(Ptr);
160   for (unsigned I = 0; I < NumFuncs; ++I) {
161     auto Hash =
162         endian::readNext<stable_hash, endianness::little, unaligned>(Ptr);
163     auto FunctionNameId =
164         endian::readNext<uint32_t, endianness::little, unaligned>(Ptr);
165     assert(FunctionMap->getNameForId(FunctionNameId) &&
166            "FunctionNameId out of range");
167     auto ModuleNameId =
168         endian::readNext<uint32_t, endianness::little, unaligned>(Ptr);
169     assert(FunctionMap->getNameForId(ModuleNameId) &&
170            "ModuleNameId out of range");
171     auto InstCount =
172         endian::readNext<uint32_t, endianness::little, unaligned>(Ptr);
173 
174     // Read IndexOperandHashes to build IndexOperandHashMap
175     auto NumIndexOperandHashes =
176         endian::readNext<uint32_t, endianness::little, unaligned>(Ptr);
177     auto IndexOperandHashMap = std::make_unique<IndexOperandHashMapType>();
178     for (unsigned J = 0; J < NumIndexOperandHashes; ++J) {
179       auto InstIndex =
180           endian::readNext<uint32_t, endianness::little, unaligned>(Ptr);
181       auto OpndIndex =
182           endian::readNext<uint32_t, endianness::little, unaligned>(Ptr);
183       auto OpndHash =
184           endian::readNext<stable_hash, endianness::little, unaligned>(Ptr);
185       assert(InstIndex < InstCount && "InstIndex out of range");
186 
187       IndexOperandHashMap->try_emplace({InstIndex, OpndIndex}, OpndHash);
188     }
189 
190     // Insert a new StableFunctionEntry into the map.
191     auto FuncEntry = std::make_unique<StableFunctionMap::StableFunctionEntry>(
192         Hash, FunctionNameId, ModuleNameId, InstCount,
193         std::move(IndexOperandHashMap));
194 
195     FunctionMap->insert(std::move(FuncEntry));
196   }
197 }
198 
serializeYAML(yaml::Output & YOS) const199 void StableFunctionMapRecord::serializeYAML(yaml::Output &YOS) const {
200   auto FuncEntries = getStableFunctionEntries(*FunctionMap);
201   SmallVector<StableFunction> Functions;
202   for (const auto *FuncEntry : FuncEntries) {
203     auto IndexOperandHashes = getStableIndexOperandHashes(FuncEntry);
204     Functions.emplace_back(
205         FuncEntry->Hash, *FunctionMap->getNameForId(FuncEntry->FunctionNameId),
206         *FunctionMap->getNameForId(FuncEntry->ModuleNameId),
207         FuncEntry->InstCount, std::move(IndexOperandHashes));
208   }
209 
210   YOS << Functions;
211 }
212 
deserializeYAML(yaml::Input & YIS)213 void StableFunctionMapRecord::deserializeYAML(yaml::Input &YIS) {
214   std::vector<StableFunction> Funcs;
215   YIS >> Funcs;
216   for (auto &Func : Funcs)
217     FunctionMap->insert(Func);
218   YIS.nextDocument();
219 }
220