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() {} - bool write(StringRef F, const FunctionSamples &S) override; + 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); + + 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; +} - // Write the file header. +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); + } +} + +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 8cbe646c16e..dc7234d8300 100644 Binary files a/test/Transforms/SampleProfile/Inputs/fnptr.binprof and b/test/Transforms/SampleProfile/Inputs/fnptr.binprof differ