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