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("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> { 39 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 *> 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 std::stable_sort( 60 FuncEntries.begin(), FuncEntries.end(), [&](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. 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 80 void StableFunctionMapRecord::serialize(raw_ostream &OS) const { 81 serialize(OS, FunctionMap.get()); 82 } 83 84 void StableFunctionMapRecord::serialize(raw_ostream &OS, 85 const StableFunctionMap *FunctionMap) { 86 support::endian::Writer Writer(OS, endianness::little); 87 88 // Write Names. 89 auto &Names = FunctionMap->getNames(); 90 uint32_t ByteSize = 4; 91 Writer.write<uint32_t>(Names.size()); 92 for (auto &Name : Names) { 93 Writer.OS << Name << '\0'; 94 ByteSize += Name.size() + 1; 95 } 96 // Align ByteSize to 4 bytes. 97 uint32_t Padding = offsetToAlignment(ByteSize, Align(4)); 98 for (uint32_t I = 0; I < Padding; ++I) 99 Writer.OS << '\0'; 100 101 // Write StableFunctionEntries whose pointers are sorted. 102 auto FuncEntries = getStableFunctionEntries(*FunctionMap); 103 Writer.write<uint32_t>(FuncEntries.size()); 104 105 for (const auto *FuncRef : FuncEntries) { 106 Writer.write<stable_hash>(FuncRef->Hash); 107 Writer.write<uint32_t>(FuncRef->FunctionNameId); 108 Writer.write<uint32_t>(FuncRef->ModuleNameId); 109 Writer.write<uint32_t>(FuncRef->InstCount); 110 111 // Emit IndexOperandHashes sorted from IndexOperandHashMap. 112 IndexOperandHashVecType IndexOperandHashes = 113 getStableIndexOperandHashes(FuncRef); 114 Writer.write<uint32_t>(IndexOperandHashes.size()); 115 for (auto &IndexOperandHash : IndexOperandHashes) { 116 Writer.write<uint32_t>(IndexOperandHash.first.first); 117 Writer.write<uint32_t>(IndexOperandHash.first.second); 118 Writer.write<stable_hash>(IndexOperandHash.second); 119 } 120 } 121 } 122 123 void StableFunctionMapRecord::deserialize(const unsigned char *&Ptr) { 124 // Assert that Ptr is 4-byte aligned 125 assert(((uintptr_t)Ptr % 4) == 0); 126 // Read Names. 127 auto NumNames = 128 endian::readNext<uint32_t, endianness::little, unaligned>(Ptr); 129 // Early exit if there is no name. 130 if (NumNames == 0) 131 return; 132 for (unsigned I = 0; I < NumNames; ++I) { 133 StringRef Name(reinterpret_cast<const char *>(Ptr)); 134 Ptr += Name.size() + 1; 135 FunctionMap->getIdOrCreateForName(Name); 136 } 137 // Align Ptr to 4 bytes. 138 Ptr = reinterpret_cast<const uint8_t *>(alignAddr(Ptr, Align(4))); 139 140 // Read StableFunctionEntries. 141 auto NumFuncs = 142 endian::readNext<uint32_t, endianness::little, unaligned>(Ptr); 143 for (unsigned I = 0; I < NumFuncs; ++I) { 144 auto Hash = 145 endian::readNext<stable_hash, endianness::little, unaligned>(Ptr); 146 auto FunctionNameId = 147 endian::readNext<uint32_t, endianness::little, unaligned>(Ptr); 148 assert(FunctionMap->getNameForId(FunctionNameId) && 149 "FunctionNameId out of range"); 150 auto ModuleNameId = 151 endian::readNext<uint32_t, endianness::little, unaligned>(Ptr); 152 assert(FunctionMap->getNameForId(ModuleNameId) && 153 "ModuleNameId out of range"); 154 auto InstCount = 155 endian::readNext<uint32_t, endianness::little, unaligned>(Ptr); 156 157 // Read IndexOperandHashes to build IndexOperandHashMap 158 auto NumIndexOperandHashes = 159 endian::readNext<uint32_t, endianness::little, unaligned>(Ptr); 160 auto IndexOperandHashMap = std::make_unique<IndexOperandHashMapType>(); 161 for (unsigned J = 0; J < NumIndexOperandHashes; ++J) { 162 auto InstIndex = 163 endian::readNext<uint32_t, endianness::little, unaligned>(Ptr); 164 auto OpndIndex = 165 endian::readNext<uint32_t, endianness::little, unaligned>(Ptr); 166 auto OpndHash = 167 endian::readNext<stable_hash, endianness::little, unaligned>(Ptr); 168 assert(InstIndex < InstCount && "InstIndex out of range"); 169 170 IndexOperandHashMap->try_emplace({InstIndex, OpndIndex}, OpndHash); 171 } 172 173 // Insert a new StableFunctionEntry into the map. 174 auto FuncEntry = std::make_unique<StableFunctionMap::StableFunctionEntry>( 175 Hash, FunctionNameId, ModuleNameId, InstCount, 176 std::move(IndexOperandHashMap)); 177 178 FunctionMap->insert(std::move(FuncEntry)); 179 } 180 } 181 182 void StableFunctionMapRecord::serializeYAML(yaml::Output &YOS) const { 183 auto FuncEntries = getStableFunctionEntries(*FunctionMap); 184 SmallVector<StableFunction> Functions; 185 for (const auto *FuncEntry : FuncEntries) { 186 auto IndexOperandHashes = getStableIndexOperandHashes(FuncEntry); 187 Functions.emplace_back( 188 FuncEntry->Hash, *FunctionMap->getNameForId(FuncEntry->FunctionNameId), 189 *FunctionMap->getNameForId(FuncEntry->ModuleNameId), 190 FuncEntry->InstCount, std::move(IndexOperandHashes)); 191 } 192 193 YOS << Functions; 194 } 195 196 void StableFunctionMapRecord::deserializeYAML(yaml::Input &YIS) { 197 std::vector<StableFunction> Funcs; 198 YIS >> Funcs; 199 for (auto &Func : Funcs) 200 FunctionMap->insert(Func); 201 YIS.nextDocument(); 202 } 203