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> { |
31 | static void mapping(IO &IO, IndexPairHash &Key) { |
32 | IO.mapRequired(Key: "InstIndex" , Val&: Key.first.first); |
33 | IO.mapRequired(Key: "OpndIndex" , Val&: Key.first.second); |
34 | IO.mapRequired(Key: "OpndHash" , Val&: Key.second); |
35 | } |
36 | }; |
37 | |
38 | template <> struct MappingTraits<StableFunction> { |
39 | static void mapping(IO &IO, StableFunction &Func) { |
40 | IO.mapRequired(Key: "Hash" , Val&: Func.Hash); |
41 | IO.mapRequired(Key: "FunctionName" , Val&: Func.FunctionName); |
42 | IO.mapRequired(Key: "ModuleName" , Val&: Func.ModuleName); |
43 | IO.mapRequired(Key: "InstCount" , Val&: Func.InstCount); |
44 | IO.mapRequired(Key: "IndexOperandHashes" , Val&: 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 *> |
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(Args: Func.get()); |
58 | |
59 | llvm::stable_sort( |
60 | Range&: FuncEntries, C: [&](auto &A, auto &B) { |
61 | return std::tuple(A->Hash, SFM.getNameForId(Id: A->ModuleNameId), |
62 | SFM.getNameForId(Id: A->FunctionNameId)) < |
63 | std::tuple(B->Hash, SFM.getNameForId(Id: B->ModuleNameId), |
64 | SFM.getNameForId(Id: B->FunctionNameId)); |
65 | }); |
66 | return FuncEntries; |
67 | } |
68 | |
69 | // Get a sorted vector of IndexOperandHashes. |
70 | static IndexOperandHashVecType getStableIndexOperandHashes( |
71 | const StableFunctionMap::StableFunctionEntry *FuncEntry) { |
72 | IndexOperandHashVecType IndexOperandHashes; |
73 | for (auto &[Indices, OpndHash] : *FuncEntry->IndexOperandHashMap) |
74 | IndexOperandHashes.emplace_back(Args&: Indices, Args&: OpndHash); |
75 | // The indices are unique, so we can just sort by the first. |
76 | llvm::sort(C&: IndexOperandHashes); |
77 | return IndexOperandHashes; |
78 | } |
79 | |
80 | void StableFunctionMapRecord::serialize( |
81 | raw_ostream &OS, std::vector<CGDataPatchItem> &PatchItems) const { |
82 | serialize(OS, FunctionMap: FunctionMap.get(), PatchItems); |
83 | } |
84 | |
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>(Val: 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>(Val: 0); |
97 | for (auto &Name : Names) |
98 | Writer.OS << Name << '\0'; |
99 | // Align current position to 4 bytes. |
100 | uint32_t Padding = offsetToAlignment(Value: Writer.OS.tell(), Alignment: 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(args: NamesByteSizeOffset, args: &NamesByteSize, args: 1); |
106 | |
107 | // Write StableFunctionEntries whose pointers are sorted. |
108 | auto FuncEntries = getStableFunctionEntries(SFM: *FunctionMap); |
109 | Writer.write<uint32_t>(Val: FuncEntries.size()); |
110 | |
111 | for (const auto *FuncRef : FuncEntries) { |
112 | Writer.write<stable_hash>(Val: FuncRef->Hash); |
113 | Writer.write<uint32_t>(Val: FuncRef->FunctionNameId); |
114 | Writer.write<uint32_t>(Val: FuncRef->ModuleNameId); |
115 | Writer.write<uint32_t>(Val: FuncRef->InstCount); |
116 | |
117 | // Emit IndexOperandHashes sorted from IndexOperandHashMap. |
118 | IndexOperandHashVecType IndexOperandHashes = |
119 | getStableIndexOperandHashes(FuncEntry: FuncRef); |
120 | Writer.write<uint32_t>(Val: IndexOperandHashes.size()); |
121 | for (auto &IndexOperandHash : IndexOperandHashes) { |
122 | Writer.write<uint32_t>(Val: IndexOperandHash.first.first); |
123 | Writer.write<uint32_t>(Val: IndexOperandHash.first.second); |
124 | Writer.write<stable_hash>(Val: IndexOperandHash.second); |
125 | } |
126 | } |
127 | } |
128 | |
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>(memory&: 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>(memory&: 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(Addr: Ptr, Alignment: 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>(memory&: Ptr); |
160 | for (unsigned I = 0; I < NumFuncs; ++I) { |
161 | auto Hash = |
162 | endian::readNext<stable_hash, endianness::little, unaligned>(memory&: Ptr); |
163 | auto FunctionNameId = |
164 | endian::readNext<uint32_t, endianness::little, unaligned>(memory&: Ptr); |
165 | assert(FunctionMap->getNameForId(FunctionNameId) && |
166 | "FunctionNameId out of range" ); |
167 | auto ModuleNameId = |
168 | endian::readNext<uint32_t, endianness::little, unaligned>(memory&: Ptr); |
169 | assert(FunctionMap->getNameForId(ModuleNameId) && |
170 | "ModuleNameId out of range" ); |
171 | auto InstCount = |
172 | endian::readNext<uint32_t, endianness::little, unaligned>(memory&: Ptr); |
173 | |
174 | // Read IndexOperandHashes to build IndexOperandHashMap |
175 | auto NumIndexOperandHashes = |
176 | endian::readNext<uint32_t, endianness::little, unaligned>(memory&: 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>(memory&: Ptr); |
181 | auto OpndIndex = |
182 | endian::readNext<uint32_t, endianness::little, unaligned>(memory&: Ptr); |
183 | auto OpndHash = |
184 | endian::readNext<stable_hash, endianness::little, unaligned>(memory&: Ptr); |
185 | assert(InstIndex < InstCount && "InstIndex out of range" ); |
186 | |
187 | IndexOperandHashMap->try_emplace(Key: {InstIndex, OpndIndex}, Args&: OpndHash); |
188 | } |
189 | |
190 | // Insert a new StableFunctionEntry into the map. |
191 | auto FuncEntry = std::make_unique<StableFunctionMap::StableFunctionEntry>( |
192 | args&: Hash, args&: FunctionNameId, args&: ModuleNameId, args&: InstCount, |
193 | args: std::move(IndexOperandHashMap)); |
194 | |
195 | FunctionMap->insert(FuncEntry: std::move(FuncEntry)); |
196 | } |
197 | } |
198 | |
199 | void StableFunctionMapRecord::serializeYAML(yaml::Output &YOS) const { |
200 | auto FuncEntries = getStableFunctionEntries(SFM: *FunctionMap); |
201 | SmallVector<StableFunction> Functions; |
202 | for (const auto *FuncEntry : FuncEntries) { |
203 | auto IndexOperandHashes = getStableIndexOperandHashes(FuncEntry); |
204 | Functions.emplace_back( |
205 | Args: FuncEntry->Hash, Args: *FunctionMap->getNameForId(Id: FuncEntry->FunctionNameId), |
206 | Args: *FunctionMap->getNameForId(Id: FuncEntry->ModuleNameId), |
207 | Args: FuncEntry->InstCount, Args: std::move(IndexOperandHashes)); |
208 | } |
209 | |
210 | YOS << Functions; |
211 | } |
212 | |
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 | |