From db271655ddef5f0108a6fdfd02cc589e7520a96c Mon Sep 17 00:00:00 2001 From: Diego Novillo Date: Tue, 13 Oct 2015 22:48:46 +0000 Subject: [PATCH] Sample profiles - Add a name table to the binary encoding. Binary encoded profiles used to encode all function names inline at every reference. This is clearly suboptimal in terms of space. This patch fixes this by adding a name table to the header of the file. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@250241 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/ProfileData/SampleProf.h | 4 +- include/llvm/ProfileData/SampleProfReader.h | 6 ++ include/llvm/ProfileData/SampleProfWriter.h | 59 +++++++++---- lib/ProfileData/SampleProf.cpp | 4 + lib/ProfileData/SampleProfReader.cpp | 28 +++++- lib/ProfileData/SampleProfWriter.cpp | 83 ++++++++++++++---- .../SampleProfile/Inputs/fnptr.binprof | Bin 115 -> 105 bytes 7 files changed, 146 insertions(+), 38 deletions(-) diff --git a/include/llvm/ProfileData/SampleProf.h b/include/llvm/ProfileData/SampleProf.h index aa715df162f..fa6656655d0 100644 --- a/include/llvm/ProfileData/SampleProf.h +++ b/include/llvm/ProfileData/SampleProf.h @@ -34,6 +34,8 @@ enum class sampleprof_error { truncated, malformed, unrecognized_format, + unsupported_writing_format, + truncated_name_table, not_implemented }; @@ -59,7 +61,7 @@ static inline uint64_t SPMagic() { uint64_t('2') << (64 - 56) | uint64_t(0xff); } -static inline uint64_t SPVersion() { return 100; } +static inline uint64_t SPVersion() { return 101; } /// Represents the relative location of an instruction. /// diff --git a/include/llvm/ProfileData/SampleProfReader.h b/include/llvm/ProfileData/SampleProfReader.h index e2976e2efab..75031e496fd 100644 --- a/include/llvm/ProfileData/SampleProfReader.h +++ b/include/llvm/ProfileData/SampleProfReader.h @@ -154,6 +154,9 @@ protected: /// \returns the read value. ErrorOr readString(); + /// Read a string indirectly via the name table. + ErrorOr readStringFromTable(); + /// \brief Return true if we've reached the end of file. bool at_eof() const { return Data >= End; } @@ -165,6 +168,9 @@ protected: /// \brief Points to the end of the buffer. const uint8_t *End; + + /// Function name table. + std::vector NameTable; }; // Represents the source position in GCC sample profiles. diff --git a/include/llvm/ProfileData/SampleProfWriter.h b/include/llvm/ProfileData/SampleProfWriter.h index 966633fcf93..1eb8156ad82 100644 --- a/include/llvm/ProfileData/SampleProfWriter.h +++ b/include/llvm/ProfileData/SampleProfWriter.h @@ -13,6 +13,7 @@ #ifndef LLVM_PROFILEDATA_SAMPLEPROFWRITER_H #define LLVM_PROFILEDATA_SAMPLEPROFWRITER_H +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/IR/Function.h" #include "llvm/IR/Module.h" @@ -35,30 +36,38 @@ public: : OS(Filename, EC, Flags) {} virtual ~SampleProfileWriter() {} - /// \brief Write sample profiles in \p S for function \p FName. + /// Write sample profiles in \p S for function \p FName. /// - /// \returns true if the file was updated successfully. False, otherwise. - virtual bool write(StringRef FName, const FunctionSamples &S) = 0; + /// \returns status code of the file update operation. + virtual std::error_code write(StringRef FName, const FunctionSamples &S) = 0; - /// \brief Write all the sample profiles in the given map of samples. + /// Write all the sample profiles in the given map of samples. /// - /// \returns true if the file was updated successfully. False, otherwise. - bool write(StringMap &ProfileMap) { - for (auto &I : ProfileMap) { + /// \returns status code of the file update operation. + std::error_code write(const StringMap &ProfileMap) { + if (std::error_code EC = writeHeader(ProfileMap)) + return EC; + + for (const auto &I : ProfileMap) { StringRef FName = I.first(); - FunctionSamples &Profile = I.second; - if (!write(FName, Profile)) - return false; + const FunctionSamples &Profile = I.second; + if (std::error_code EC = write(FName, Profile)) + return EC; } - return true; + return sampleprof_error::success; } - /// \brief Profile writer factory. Create a new writer based on the value of - /// \p Format. + /// Profile writer factory. + /// + /// Create a new writer based on the value of \p Format. static ErrorOr> create(StringRef Filename, SampleProfileFormat Format); protected: + /// \brief Write a file header for the profile file. + virtual std::error_code + writeHeader(const StringMap &ProfileMap) = 0; + /// \brief Output stream where to emit the profile to. raw_fd_ostream OS; }; @@ -69,7 +78,13 @@ public: SampleProfileWriterText(StringRef F, std::error_code &EC) : SampleProfileWriter(F, EC, sys::fs::F_Text), Indent(0) {} - bool write(StringRef FName, const FunctionSamples &S) override; + std::error_code write(StringRef FName, const FunctionSamples &S) override; + +protected: + std::error_code + writeHeader(const StringMap &ProfileMap) override { + return sampleprof_error::success; + } private: /// Indent level to use when writing. @@ -81,9 +96,21 @@ private: /// \brief Sample-based profile writer (binary format). class SampleProfileWriterBinary : public SampleProfileWriter { public: - SampleProfileWriterBinary(StringRef F, std::error_code &EC); + SampleProfileWriterBinary(StringRef F, std::error_code &EC) + : SampleProfileWriter(F, EC, sys::fs::F_None), NameTable() {} + + std::error_code write(StringRef F, const FunctionSamples &S) override; + +protected: + std::error_code + writeHeader(const StringMap &ProfileMap) override; + std::error_code writeNameIdx(StringRef FName); + +private: + void addName(StringRef FName); + void addNames(const FunctionSamples &S); - bool write(StringRef F, const FunctionSamples &S) override; + MapVector NameTable; }; } // End namespace sampleprof diff --git a/lib/ProfileData/SampleProf.cpp b/lib/ProfileData/SampleProf.cpp index 027fa81790b..c6960ba4bd9 100644 --- a/lib/ProfileData/SampleProf.cpp +++ b/lib/ProfileData/SampleProf.cpp @@ -38,6 +38,10 @@ class SampleProfErrorCategoryType : public std::error_category { return "Malformed profile data"; case sampleprof_error::unrecognized_format: return "Unrecognized profile encoding format"; + case sampleprof_error::unsupported_writing_format: + return "Profile encoding format unsupported for writing operations"; + case sampleprof_error::truncated_name_table: + return "Truncated function name table"; case sampleprof_error::not_implemented: return "Unimplemented feature"; } diff --git a/lib/ProfileData/SampleProfReader.cpp b/lib/ProfileData/SampleProfReader.cpp index c620d4ca84a..a058274e8ee 100644 --- a/lib/ProfileData/SampleProfReader.cpp +++ b/lib/ProfileData/SampleProfReader.cpp @@ -378,6 +378,16 @@ ErrorOr SampleProfileReaderBinary::readString() { return Str; } +ErrorOr SampleProfileReaderBinary::readStringFromTable() { + std::error_code EC; + auto Idx = readNumber(); + if (std::error_code EC = Idx.getError()) + return EC; + if (*Idx >= NameTable.size()) + return sampleprof_error::truncated_name_table; + return NameTable[*Idx]; +} + std::error_code SampleProfileReaderBinary::readProfile(FunctionSamples &FProfile) { auto Val = readNumber(); @@ -413,7 +423,7 @@ SampleProfileReaderBinary::readProfile(FunctionSamples &FProfile) { return EC; for (unsigned J = 0; J < *NumCalls; ++J) { - auto CalledFunction(readString()); + auto CalledFunction(readStringFromTable()); if (std::error_code EC = CalledFunction.getError()) return EC; @@ -442,7 +452,7 @@ SampleProfileReaderBinary::readProfile(FunctionSamples &FProfile) { if (std::error_code EC = Discriminator.getError()) return EC; - auto FName(readString()); + auto FName(readStringFromTable()); if (std::error_code EC = FName.getError()) return EC; @@ -457,7 +467,7 @@ SampleProfileReaderBinary::readProfile(FunctionSamples &FProfile) { std::error_code SampleProfileReaderBinary::read() { while (!at_eof()) { - auto FName(readString()); + auto FName(readStringFromTable()); if (std::error_code EC = FName.getError()) return EC; @@ -489,6 +499,18 @@ std::error_code SampleProfileReaderBinary::readHeader() { else if (*Version != SPVersion()) return sampleprof_error::unsupported_version; + // Read the name table. + auto Size = readNumber(); + if (std::error_code EC = Size.getError()) + return EC; + NameTable.reserve(*Size); + for (size_t I = 0; I < *Size; ++I) { + auto Name(readString()); + if (std::error_code EC = Name.getError()) + return EC; + NameTable.push_back(*Name); + } + return sampleprof_error::success; } diff --git a/lib/ProfileData/SampleProfWriter.cpp b/lib/ProfileData/SampleProfWriter.cpp index 6be884b13e0..d3001aac3f6 100644 --- a/lib/ProfileData/SampleProfWriter.cpp +++ b/lib/ProfileData/SampleProfWriter.cpp @@ -30,7 +30,8 @@ using namespace llvm::sampleprof; using namespace llvm; /// \brief Write samples to a text file. -bool SampleProfileWriterText::write(StringRef FName, const FunctionSamples &S) { +std::error_code SampleProfileWriterText::write(StringRef FName, + const FunctionSamples &S) { OS << FName << ":" << S.getTotalSamples(); if (Indent == 0) OS << ":" << S.getHeadSamples(); @@ -61,31 +62,74 @@ bool SampleProfileWriterText::write(StringRef FName, const FunctionSamples &S) { OS << Loc.LineOffset << ": "; else OS << Loc.LineOffset << "." << Loc.Discriminator << ": "; - write(Loc.CalleeName, CalleeSamples); + if (std::error_code EC = write(Loc.CalleeName, CalleeSamples)) + return EC; } Indent -= 1; - return true; + return sampleprof_error::success; } -SampleProfileWriterBinary::SampleProfileWriterBinary(StringRef F, - std::error_code &EC) - : SampleProfileWriter(F, EC, sys::fs::F_None) { - if (EC) - return; +std::error_code SampleProfileWriterBinary::writeNameIdx(StringRef FName) { + const auto &ret = NameTable.find(FName); + if (ret == NameTable.end()) + return sampleprof_error::truncated_name_table; + encodeULEB128(ret->second, OS); + return sampleprof_error::success; +} + +void SampleProfileWriterBinary::addName(StringRef FName) { + auto NextIdx = NameTable.size(); + NameTable.insert(std::make_pair(FName, NextIdx)); +} + +void SampleProfileWriterBinary::addNames(const FunctionSamples &S) { + // Add all the names in indirect call targets. + for (const auto &I : S.getBodySamples()) { + const SampleRecord &Sample = I.second; + for (const auto &J : Sample.getCallTargets()) + addName(J.first()); + } + + // Recursively add all the names for inlined callsites. + for (const auto &J : S.getCallsiteSamples()) { + CallsiteLocation Loc = J.first; + const FunctionSamples &CalleeSamples = J.second; + addName(Loc.CalleeName); + addNames(CalleeSamples); + } +} - // Write the file header. +std::error_code SampleProfileWriterBinary::writeHeader( + const StringMap &ProfileMap) { + // Write file magic identifier. encodeULEB128(SPMagic(), OS); encodeULEB128(SPVersion(), OS); + + // Generate the name table for all the functions referenced in the profile. + for (const auto &I : ProfileMap) { + addName(I.first()); + addNames(I.second); + } + + // Write out the name table. + encodeULEB128(NameTable.size(), OS); + for (auto N : NameTable) { + OS << N.first; + encodeULEB128(0, OS); + } + + return sampleprof_error::success; } /// \brief Write samples to a binary file. /// /// \returns true if the samples were written successfully, false otherwise. -bool SampleProfileWriterBinary::write(StringRef FName, - const FunctionSamples &S) { - OS << FName; - encodeULEB128(0, OS); +std::error_code SampleProfileWriterBinary::write(StringRef FName, + const FunctionSamples &S) { + if (std::error_code EC = writeNameIdx(FName)) + return EC; + encodeULEB128(S.getTotalSamples(), OS); encodeULEB128(S.getHeadSamples(), OS); encodeULEB128(S.getBodySamples().size(), OS); @@ -99,10 +143,10 @@ bool SampleProfileWriterBinary::write(StringRef FName, encodeULEB128(Sample.getSamples(), OS); encodeULEB128(Sample.getCallTargets().size(), OS); for (const auto &J : Sample.getCallTargets()) { - std::string Callee = J.first(); + StringRef Callee = J.first(); unsigned CalleeSamples = J.second; - OS << Callee; - encodeULEB128(0, OS); + if (std::error_code EC = writeNameIdx(Callee)) + return EC; encodeULEB128(CalleeSamples, OS); } } @@ -114,10 +158,11 @@ bool SampleProfileWriterBinary::write(StringRef FName, const FunctionSamples &CalleeSamples = J.second; encodeULEB128(Loc.LineOffset, OS); encodeULEB128(Loc.Discriminator, OS); - write(Loc.CalleeName, CalleeSamples); + if (std::error_code EC = write(Loc.CalleeName, CalleeSamples)) + return EC; } - return true; + return sampleprof_error::success; } /// \brief Create a sample profile writer based on the specified format. @@ -138,6 +183,8 @@ SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) { Writer.reset(new SampleProfileWriterBinary(Filename, EC)); else if (Format == SPF_Text) Writer.reset(new SampleProfileWriterText(Filename, EC)); + else if (Format == SPF_GCC) + EC = sampleprof_error::unsupported_writing_format; else EC = sampleprof_error::unrecognized_format; diff --git a/test/Transforms/SampleProfile/Inputs/fnptr.binprof b/test/Transforms/SampleProfile/Inputs/fnptr.binprof index 8cbe646c16ee598d8c4eaad145635cbfd7a8fabc..dc7234d8300828c853036e310bd2e83a6d651807 100644 GIT binary patch literal 105 zcmezW^uoq3C#S3kPG!zb%*qE0$xY16o2Vcf5FcfnmY<)=@SO$7N=husWZ2Km%D9<>ft3MB ouro|!VPIhb5)3ef^KBlnFfuYcf(U_ioSnxwmm4THmz#kB06;q)?*IS* -- 2.34.1