//===-- StableFunctionMapRecord.cpp ---------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This implements the functionality for the StableFunctionMapRecord class, // including methods for serialization and deserialization of stable function // maps to and from raw and YAML streams. It also includes utilities for // managing function entries and their metadata. // //===----------------------------------------------------------------------===// #include "llvm/CGData/StableFunctionMapRecord.h" #include "llvm/Support/EndianStream.h" #define DEBUG_TYPE "stable-function-map-record" using namespace llvm; using namespace llvm::support; LLVM_YAML_IS_SEQUENCE_VECTOR(IndexPairHash) LLVM_YAML_IS_SEQUENCE_VECTOR(StableFunction) namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &IO, IndexPairHash &Key) { IO.mapRequired("InstIndex", Key.first.first); IO.mapRequired("OpndIndex", Key.first.second); IO.mapRequired("OpndHash", Key.second); } }; template <> struct MappingTraits { static void mapping(IO &IO, StableFunction &Func) { IO.mapRequired("Hash", Func.Hash); IO.mapRequired("FunctionName", Func.FunctionName); IO.mapRequired("ModuleName", Func.ModuleName); IO.mapRequired("InstCount", Func.InstCount); IO.mapRequired("IndexOperandHashes", Func.IndexOperandHashes); } }; } // namespace yaml } // namespace llvm // Get a sorted vector of StableFunctionEntry pointers. static SmallVector getStableFunctionEntries(const StableFunctionMap &SFM) { SmallVector FuncEntries; for (const auto &P : SFM.getFunctionMap()) for (auto &Func : P.second.Entries) FuncEntries.emplace_back(Func.get()); llvm::stable_sort( FuncEntries, [&](auto &A, auto &B) { return std::tuple(A->Hash, SFM.getNameForId(A->ModuleNameId), SFM.getNameForId(A->FunctionNameId)) < std::tuple(B->Hash, SFM.getNameForId(B->ModuleNameId), SFM.getNameForId(B->FunctionNameId)); }); return FuncEntries; } // Get a sorted vector of IndexOperandHashes. static IndexOperandHashVecType getStableIndexOperandHashes( const StableFunctionMap::StableFunctionEntry *FuncEntry) { IndexOperandHashVecType IndexOperandHashes; for (auto &[Indices, OpndHash] : *FuncEntry->IndexOperandHashMap) IndexOperandHashes.emplace_back(Indices, OpndHash); // The indices are unique, so we can just sort by the first. llvm::sort(IndexOperandHashes); return IndexOperandHashes; } void StableFunctionMapRecord::serialize( raw_ostream &OS, std::vector &PatchItems) const { serialize(OS, FunctionMap.get(), PatchItems); } void StableFunctionMapRecord::serialize( raw_ostream &OS, const StableFunctionMap *FunctionMap, std::vector &PatchItems) { support::endian::Writer Writer(OS, endianness::little); // Write Names. ArrayRef Names = FunctionMap->getNames(); Writer.write(Names.size()); // Remember the position, write back the total size of Names, so we can skip // reading them if needed. const uint64_t NamesByteSizeOffset = Writer.OS.tell(); Writer.write(0); for (auto &Name : Names) Writer.OS << Name << '\0'; // Align current position to 4 bytes. uint32_t Padding = offsetToAlignment(Writer.OS.tell(), Align(4)); for (uint32_t I = 0; I < Padding; ++I) Writer.OS << '\0'; const auto NamesByteSize = Writer.OS.tell() - NamesByteSizeOffset - sizeof(NamesByteSizeOffset); PatchItems.emplace_back(NamesByteSizeOffset, &NamesByteSize, 1); // Write StableFunctionEntries whose pointers are sorted. auto FuncEntries = getStableFunctionEntries(*FunctionMap); Writer.write(FuncEntries.size()); for (const auto *FuncRef : FuncEntries) Writer.write(FuncRef->Hash); std::vector IndexOperandHashesOffsets; IndexOperandHashesOffsets.reserve(FuncEntries.size()); for (const auto *FuncRef : FuncEntries) { Writer.write(FuncRef->FunctionNameId); Writer.write(FuncRef->ModuleNameId); Writer.write(FuncRef->InstCount); const uint64_t Offset = Writer.OS.tell(); IndexOperandHashesOffsets.push_back(Offset); Writer.write(0); } const uint64_t IndexOperandHashesByteSizeOffset = Writer.OS.tell(); Writer.write(0); for (size_t I = 0; I < FuncEntries.size(); ++I) { const uint64_t Offset = Writer.OS.tell() - IndexOperandHashesOffsets[I]; PatchItems.emplace_back(IndexOperandHashesOffsets[I], &Offset, 1); // Emit IndexOperandHashes sorted from IndexOperandHashMap. const auto *FuncRef = FuncEntries[I]; IndexOperandHashVecType IndexOperandHashes = getStableIndexOperandHashes(FuncRef); Writer.write(IndexOperandHashes.size()); for (auto &IndexOperandHash : IndexOperandHashes) { Writer.write(IndexOperandHash.first.first); Writer.write(IndexOperandHash.first.second); Writer.write(IndexOperandHash.second); } } // Write the total size of IndexOperandHashes. const uint64_t IndexOperandHashesByteSize = Writer.OS.tell() - IndexOperandHashesByteSizeOffset - sizeof(uint64_t); PatchItems.emplace_back(IndexOperandHashesByteSizeOffset, &IndexOperandHashesByteSize, 1); } void StableFunctionMapRecord::deserializeEntry(const unsigned char *Ptr, stable_hash Hash, StableFunctionMap *FunctionMap) { auto FunctionNameId = endian::readNext(Ptr); if (FunctionMap->ReadStableFunctionMapNames) assert(FunctionMap->getNameForId(FunctionNameId) && "FunctionNameId out of range"); auto ModuleNameId = endian::readNext(Ptr); if (FunctionMap->ReadStableFunctionMapNames) assert(FunctionMap->getNameForId(ModuleNameId) && "ModuleNameId out of range"); auto InstCount = endian::readNext(Ptr); // Read IndexOperandHashes to build IndexOperandHashMap auto CurrentPosition = reinterpret_cast(Ptr); auto IndexOperandHashesOffset = endian::readNext(Ptr); auto *IndexOperandHashesPtr = reinterpret_cast( CurrentPosition + IndexOperandHashesOffset); auto NumIndexOperandHashes = endian::readNext( IndexOperandHashesPtr); auto IndexOperandHashMap = std::make_unique(); for (unsigned J = 0; J < NumIndexOperandHashes; ++J) { auto InstIndex = endian::readNext( IndexOperandHashesPtr); auto OpndIndex = endian::readNext( IndexOperandHashesPtr); auto OpndHash = endian::readNext( IndexOperandHashesPtr); assert(InstIndex < InstCount && "InstIndex out of range"); IndexOperandHashMap->try_emplace({InstIndex, OpndIndex}, OpndHash); } // Insert a new StableFunctionEntry into the map. auto FuncEntry = std::make_unique( Hash, FunctionNameId, ModuleNameId, InstCount, std::move(IndexOperandHashMap)); FunctionMap->insert(std::move(FuncEntry)); } void StableFunctionMapRecord::deserialize(const unsigned char *&Ptr, bool Lazy) { // Assert that Ptr is 4-byte aligned assert(((uintptr_t)Ptr % 4) == 0); // Read Names. auto NumNames = endian::readNext(Ptr); // Early exit if there is no name. if (NumNames == 0) return; const auto NamesByteSize = endian::readNext(Ptr); const auto NamesOffset = reinterpret_cast(Ptr); if (FunctionMap->ReadStableFunctionMapNames) { for (unsigned I = 0; I < NumNames; ++I) { StringRef Name(reinterpret_cast(Ptr)); Ptr += Name.size() + 1; FunctionMap->getIdOrCreateForName(Name); } // Align Ptr to 4 bytes. Ptr = reinterpret_cast(alignAddr(Ptr, Align(4))); assert(reinterpret_cast(Ptr) - NamesOffset == NamesByteSize && "NamesByteSize does not match the actual size of names"); } else { // skip reading Names by advancing the pointer. Ptr = reinterpret_cast(NamesOffset + NamesByteSize); } // Read StableFunctionEntries. auto NumFuncs = endian::readNext(Ptr); auto FixedSizeFieldsOffset = reinterpret_cast(Ptr) + NumFuncs * sizeof(stable_hash); constexpr uint32_t FixedSizeFieldsSizePerEntry = // FunctionNameId sizeof(uint32_t) + // ModuleNameId sizeof(uint32_t) + // InstCount sizeof(uint32_t) + // Relative offset to IndexOperandHashes sizeof(uint64_t); for (unsigned I = 0; I < NumFuncs; ++I) { auto Hash = endian::readNext(Ptr); if (Lazy) { auto It = FunctionMap->HashToFuncs.try_emplace(Hash).first; StableFunctionMap::EntryStorage &Storage = It->second; Storage.Offsets.push_back(FixedSizeFieldsOffset); } else { deserializeEntry( reinterpret_cast(FixedSizeFieldsOffset), Hash, FunctionMap.get()); } FixedSizeFieldsOffset += FixedSizeFieldsSizePerEntry; } // Update Ptr to the end of the serialized map to meet the expectation of // CodeGenDataReader. Ptr = reinterpret_cast(FixedSizeFieldsOffset); auto IndexOperandHashesByteSize = endian::readNext(Ptr); Ptr = reinterpret_cast( reinterpret_cast(Ptr) + IndexOperandHashesByteSize); } void StableFunctionMapRecord::deserialize(const unsigned char *&Ptr) { deserialize(Ptr, /*Lazy=*/false); } void StableFunctionMapRecord::lazyDeserialize( std::shared_ptr Buffer, uint64_t Offset) { const auto *Ptr = reinterpret_cast( reinterpret_cast(Buffer->getBufferStart()) + Offset); deserialize(Ptr, /*Lazy=*/true); FunctionMap->Buffer = std::move(Buffer); } void StableFunctionMapRecord::serializeYAML(yaml::Output &YOS) const { auto FuncEntries = getStableFunctionEntries(*FunctionMap); SmallVector Functions; for (const auto *FuncEntry : FuncEntries) { auto IndexOperandHashes = getStableIndexOperandHashes(FuncEntry); Functions.emplace_back( FuncEntry->Hash, *FunctionMap->getNameForId(FuncEntry->FunctionNameId), *FunctionMap->getNameForId(FuncEntry->ModuleNameId), FuncEntry->InstCount, std::move(IndexOperandHashes)); } YOS << Functions; } void StableFunctionMapRecord::deserializeYAML(yaml::Input &YIS) { std::vector Funcs; YIS >> Funcs; for (auto &Func : Funcs) FunctionMap->insert(Func); YIS.nextDocument(); }