Index: include/llvm/ProfileData/SampleProf.h =================================================================== --- include/llvm/ProfileData/SampleProf.h +++ include/llvm/ProfileData/SampleProf.h @@ -18,12 +18,14 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/Module.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" #include #include #include @@ -49,7 +51,9 @@ truncated_name_table, not_implemented, counter_overflow, - ostream_seek_unsupported + ostream_seek_unsupported, + compress_failed, + uncompress_failed }; inline std::error_code make_error_code(sampleprof_error E) { @@ -83,6 +87,7 @@ SPF_Text = 0x1, SPF_Compact_Binary = 0x2, SPF_GCC = 0x3, + SPF_Ext_Binary = 0x4, SPF_Binary = 0xff }; @@ -105,6 +110,20 @@ static inline uint64_t SPVersion() { return 103; } +enum SecType { + SecProfSummary = 0, + SecNameTable = 1, + SecFuncProfile = 2, + SecSymbolWhiteList = 3 +}; + +struct SecHdrTableEntry { + SecType Type; + uint64_t Flag; + uint64_t Offset; + uint64_t Size; +}; + /// Represents the relative location of an instruction. /// /// Instruction locations are specified by the line offset from the @@ -550,6 +569,42 @@ SamplesWithLocList V; }; +/// SymbolWhiteList records the list of function symbols shown up +/// in the binary used to generate the profile. It is useful to +/// to discriminate a function being so cold as not to shown up +/// in the profile and a function newly added. +class SymbolWhiteList { +public: + /// copy indicates whether we need to copy the underlying memory + /// for the input Name. + void add(StringRef Name, bool copy = false) { + if (!copy) { + Syms.insert(Name); + return; + } + Syms.insert(Name.copy(Allocator)); + } + + bool contains(StringRef Name) { return Syms.count(Name); } + + void merge(const SymbolWhiteList &List) { + for (auto Sym : List.Syms) + add(Sym, true); + } + + unsigned size() { return Syms.size(); } + + std::error_code read(uint64_t CompressSize, uint64_t UncompressSize, + const uint8_t *Data); + std::error_code write(raw_ostream &OS); + void print(raw_ostream &OS) const; + void dump() const; + +private: + DenseSet Syms; + BumpPtrAllocator Allocator; +}; + } // end namespace sampleprof } // end namespace llvm Index: include/llvm/ProfileData/SampleProfReader.h =================================================================== --- include/llvm/ProfileData/SampleProfReader.h +++ include/llvm/ProfileData/SampleProfReader.h @@ -326,6 +326,10 @@ /// \brief Return the profile format. SampleProfileFormat getFormat() { return Format; } + virtual std::unique_ptr getSymbolWhiteList() { + return nullptr; + }; + protected: /// Map every function to its associated profile. /// @@ -416,6 +420,15 @@ /// Read the contents of the given profile instance. std::error_code readProfile(FunctionSamples &FProfile); + /// Read the contents of Magic number and Version number. + std::error_code readMagicIdent(); + + /// Read profile summary. + std::error_code readSummary(); + + /// Read the whole name table. + virtual std::error_code readNameTable() = 0; + /// Points to the current location in the buffer. const uint8_t *Data = nullptr; @@ -426,12 +439,6 @@ std::error_code readSummaryEntry(std::vector &Entries); virtual std::error_code verifySPMagic(uint64_t Magic) = 0; - /// Read profile summary. - std::error_code readSummary(); - - /// Read the whole name table. - virtual std::error_code readNameTable() = 0; - /// Read a string indirectly via the name table. virtual ErrorOr readStringFromTable() = 0; }; @@ -441,16 +448,59 @@ /// Function name table. std::vector NameTable; virtual std::error_code verifySPMagic(uint64_t Magic) override; - virtual std::error_code readNameTable() override; /// Read a string indirectly via the name table. virtual ErrorOr readStringFromTable() override; +protected: + virtual std::error_code readNameTable() override; + +public: + SampleProfileReaderRawBinary(std::unique_ptr B, LLVMContext &C, + SampleProfileFormat Format = SPF_Binary) + : SampleProfileReaderBinary(std::move(B), C, Format) {} + + /// \brief Return true if \p Buffer is in the format supported by this class. + static bool hasFormat(const MemoryBuffer &Buffer); +}; + +/// SampleProfileReaderExtBinary is a binary format which is more easily to be +/// extend than SampleProfileReaderRawBinary. +/// SampleProfileReaderExtBinary format is organized in sections except the +/// magic and version number at the beginning. There is a section table before +/// all the sections, and each entry in the table describes the entry type, +/// start, size and attributes. The format in each section is defined by the +/// section itself. +/// It is easy to add a new section while maintaining the backward +/// compatibility of the profile. Nothing extra needs to be done. If we want +/// to extend an existing section, like add cache misses information in +/// addition to the sample count in the profile body, we can add a new section +/// with the extension and retire the existing section, and we could choose +/// to keep the parser of the old section if we want the reader to be able +/// to read both new and old format profile. +class SampleProfileReaderExtBinary : public SampleProfileReaderRawBinary { +private: + std::vector SecHdrTable; + std::unique_ptr SymWhiteList; + std::error_code readSecHdrTableEntry(); + std::error_code readSecHdrTable(); + virtual std::error_code readHeader() override; + virtual std::error_code verifySPMagic(uint64_t Magic) override; + public: - SampleProfileReaderRawBinary(std::unique_ptr B, LLVMContext &C) - : SampleProfileReaderBinary(std::move(B), C, SPF_Binary) {} + SampleProfileReaderExtBinary(std::unique_ptr B, LLVMContext &C) + : SampleProfileReaderRawBinary(std::move(B), C, SPF_Ext_Binary) {} /// \brief Return true if \p Buffer is in the format supported by this class. static bool hasFormat(const MemoryBuffer &Buffer); + + /// Read sample profiles in extensible format from the associated file. + std::error_code read() override; + + std::error_code readSymbolWhiteList(); + + virtual std::unique_ptr getSymbolWhiteList() override { + return std::move(SymWhiteList); + }; }; class SampleProfileReaderCompactBinary : public SampleProfileReaderBinary { Index: include/llvm/ProfileData/SampleProfWriter.h =================================================================== --- include/llvm/ProfileData/SampleProfWriter.h +++ include/llvm/ProfileData/SampleProfWriter.h @@ -36,7 +36,7 @@ /// Write sample profiles in \p S. /// /// \returns status code of the file update operation. - virtual std::error_code write(const FunctionSamples &S) = 0; + virtual std::error_code writeSample(const FunctionSamples &S) = 0; /// Write all the sample profiles in the given map of samples. /// @@ -56,6 +56,8 @@ static ErrorOr> create(std::unique_ptr &OS, SampleProfileFormat Format); + virtual void setSymbolWhiteList(SymbolWhiteList *WL) {} + protected: SampleProfileWriter(std::unique_ptr &OS) : OutputStream(std::move(OS)) {} @@ -64,6 +66,10 @@ virtual std::error_code writeHeader(const StringMap &ProfileMap) = 0; + // Write function profiles to the profile file. + virtual std::error_code + writeFuncProfiles(const StringMap &ProfileMap); + /// Output stream where to emit the profile to. std::unique_ptr OutputStream; @@ -72,12 +78,15 @@ /// Compute summary for this profile. void computeSummary(const StringMap &ProfileMap); + + /// Profile format. + SampleProfileFormat Format; }; /// Sample-based profile writer (text format). class SampleProfileWriterText : public SampleProfileWriter { public: - std::error_code write(const FunctionSamples &S) override; + std::error_code writeSample(const FunctionSamples &S) override; protected: SampleProfileWriterText(std::unique_ptr &OS) @@ -102,13 +111,13 @@ /// Sample-based profile writer (binary format). class SampleProfileWriterBinary : public SampleProfileWriter { public: - virtual std::error_code write(const FunctionSamples &S) override; + virtual std::error_code writeSample(const FunctionSamples &S) override; SampleProfileWriterBinary(std::unique_ptr &OS) : SampleProfileWriter(OS) {} protected: virtual std::error_code writeNameTable() = 0; - virtual std::error_code writeMagicIdent() = 0; + virtual std::error_code writeMagicIdent(SampleProfileFormat Format); virtual std::error_code writeHeader(const StringMap &ProfileMap) override; std::error_code writeSummary(); @@ -118,10 +127,10 @@ MapVector NameTable; -private: void addName(StringRef FName); void addNames(const FunctionSamples &S); +private: friend ErrorOr> SampleProfileWriter::create(std::unique_ptr &OS, SampleProfileFormat Format); @@ -132,7 +141,28 @@ protected: virtual std::error_code writeNameTable() override; - virtual std::error_code writeMagicIdent() override; +}; + +class SampleProfileWriterExtBinary : public SampleProfileWriterRawBinary { + using SampleProfileWriterRawBinary::SampleProfileWriterRawBinary; + +public: + virtual std::error_code + write(const StringMap &ProfileMap) override; + virtual void setSymbolWhiteList(SymbolWhiteList *WL) override { + SymWhiteList = WL; + }; + +private: + void allocSecHdrTable(); + std::error_code writeSecHdrTable(); + virtual std::error_code + writeHeader(const StringMap &ProfileMap) override; + + uint64_t FileStart; + uint64_t SecHdrTableOffset; + std::vector SecHdrTable; + SymbolWhiteList *SymWhiteList = nullptr; }; // CompactBinary is a compact format of binary profile which both reduces @@ -169,7 +199,7 @@ using SampleProfileWriterBinary::SampleProfileWriterBinary; public: - virtual std::error_code write(const FunctionSamples &S) override; + virtual std::error_code writeSample(const FunctionSamples &S) override; virtual std::error_code write(const StringMap &ProfileMap) override; @@ -181,7 +211,6 @@ /// towards profile start. uint64_t TableOffset; virtual std::error_code writeNameTable() override; - virtual std::error_code writeMagicIdent() override; virtual std::error_code writeHeader(const StringMap &ProfileMap) override; std::error_code writeFuncOffsetTable(); Index: lib/ProfileData/SampleProf.cpp =================================================================== --- lib/ProfileData/SampleProf.cpp +++ lib/ProfileData/SampleProf.cpp @@ -15,8 +15,11 @@ #include "llvm/Config/llvm-config.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/raw_ostream.h" #include @@ -66,6 +69,10 @@ return "Counter overflow"; case sampleprof_error::ostream_seek_unsupported: return "Ostream does not support seek"; + case sampleprof_error::compress_failed: + return "Compress failure"; + case sampleprof_error::uncompress_failed: + return "Uncompress failure"; } llvm_unreachable("A value of sampleprof_error has no message."); } @@ -188,3 +195,57 @@ #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void FunctionSamples::dump() const { print(dbgs(), 0); } #endif + +std::error_code SymbolWhiteList::read(uint64_t CompressSize, + uint64_t UncompressSize, + const uint8_t *Data) { + StringRef CompressedStrings(reinterpret_cast(Data), + CompressSize); + char *Buffer = Allocator.Allocate(UncompressSize); + llvm::Error E = zlib::uncompress(CompressedStrings, Buffer, UncompressSize); + if (E) + return sampleprof_error::uncompress_failed; + uint64_t Size = 0; + while (Size < UncompressSize) { + StringRef Str(Buffer + Size); + add(Str); + Size += Str.size() + 1; + } + return sampleprof_error::success; +} + +std::error_code SymbolWhiteList::write(raw_ostream &OS) { + // Sort the symbols before doing compression. It will make the + // compression much more effective. + std::vector SortedList; + SortedList.insert(SortedList.begin(), Syms.begin(), Syms.end()); + std::sort(SortedList.begin(), SortedList.end()); + + std::string UncompressedStrings; + for (auto &Sym : SortedList) { + UncompressedStrings.append(Sym.str()); + UncompressedStrings.append(1, '\0'); + } + + SmallString<128> CompressedStrings; + llvm::Error E = zlib::compress(UncompressedStrings, CompressedStrings, + zlib::BestSizeCompression); + if (E) + return sampleprof_error::compress_failed; + encodeULEB128(UncompressedStrings.size(), OS); + encodeULEB128(CompressedStrings.size(), OS); + OS << CompressedStrings.str(); + return sampleprof_error::success; +} + +void SymbolWhiteList::print(raw_ostream &OS) const { + OS << "======== Dump symbol whitelist ========\n"; + std::vector SortedList; + SortedList.insert(SortedList.begin(), Syms.begin(), Syms.end()); + std::sort(SortedList.begin(), SortedList.end()); + + for (auto &Sym : SortedList) + OS << Sym << "\n"; +} + +void SymbolWhiteList::dump() const { print(dbgs()); } Index: lib/ProfileData/SampleProfReader.cpp =================================================================== --- lib/ProfileData/SampleProfReader.cpp +++ lib/ProfileData/SampleProfReader.cpp @@ -467,6 +467,52 @@ return sampleprof_error::success; } +std::error_code SampleProfileReaderExtBinary::readSymbolWhiteList() { + auto UncompressSize = readNumber(); + if (std::error_code EC = UncompressSize.getError()) + return EC; + + auto CompressSize = readNumber(); + if (std::error_code EC = CompressSize.getError()) + return EC; + + if (!SymWhiteList) + SymWhiteList = std::make_unique(); + + if (std::error_code EC = + SymWhiteList->read(*CompressSize, *UncompressSize, Data)) + return EC; + Data = Data + *CompressSize; + return sampleprof_error::success; +} + +std::error_code SampleProfileReaderExtBinary::read() { + const uint8_t *BufStart = + reinterpret_cast(Buffer->getBufferStart()); + + for (auto &Entry : SecHdrTable) { + Data = BufStart + Entry.Offset; + switch (Entry.Type) { + case SecFuncProfile: + while (Data < BufStart + Entry.Offset + Entry.Size) { + if (std::error_code EC = readFuncProfile()) + return EC; + } + break; + case SecSymbolWhiteList: + if (std::error_code EC = readSymbolWhiteList()) + return EC; + break; + default: + continue; + } + if (Data != BufStart + Entry.Offset + Entry.Size) + return sampleprof_error::malformed; + } + + return sampleprof_error::success; +} + std::error_code SampleProfileReaderCompactBinary::read() { std::vector OffsetsToUse; if (UseAllFuncs) { @@ -501,6 +547,12 @@ return sampleprof_error::bad_magic; } +std::error_code SampleProfileReaderExtBinary::verifySPMagic(uint64_t Magic) { + if (Magic == SPMagic(SPF_Ext_Binary)) + return sampleprof_error::success; + return sampleprof_error::bad_magic; +} + std::error_code SampleProfileReaderCompactBinary::verifySPMagic(uint64_t Magic) { if (Magic == SPMagic(SPF_Compact_Binary)) @@ -537,10 +589,78 @@ return sampleprof_error::success; } -std::error_code SampleProfileReaderBinary::readHeader() { - Data = reinterpret_cast(Buffer->getBufferStart()); - End = Data + Buffer->getBufferSize(); +std::error_code SampleProfileReaderExtBinary::readSecHdrTableEntry() { + SecHdrTableEntry Entry; + auto Type = readUnencodedNumber(); + if (std::error_code EC = Type.getError()) + return EC; + Entry.Type = static_cast(*Type); + + auto Flag = readUnencodedNumber(); + if (std::error_code EC = Flag.getError()) + return EC; + Entry.Flag = *Flag; + + auto Offset = readUnencodedNumber(); + if (std::error_code EC = Offset.getError()) + return EC; + Entry.Offset = *Offset; + + auto Size = readUnencodedNumber(); + if (std::error_code EC = Size.getError()) + return EC; + Entry.Size = *Size; + + SecHdrTable.push_back(std::move(Entry)); + return sampleprof_error::success; +} +std::error_code SampleProfileReaderExtBinary::readSecHdrTable() { + auto EntryNum = readUnencodedNumber(); + if (std::error_code EC = EntryNum.getError()) + return EC; + + for (uint32_t i = 0; i < (*EntryNum); i++) + if (std::error_code EC = readSecHdrTableEntry()) + return EC; + + return sampleprof_error::success; +} + +std::error_code SampleProfileReaderExtBinary::readHeader() { + const uint8_t *BufStart = + reinterpret_cast(Buffer->getBufferStart()); + Data = BufStart; + End = BufStart + Buffer->getBufferSize(); + + if (std::error_code EC = readMagicIdent()) + return EC; + + if (std::error_code EC = readSecHdrTable()) + return EC; + + for (auto &Entry : SecHdrTable) { + Data = BufStart + Entry.Offset; + switch (Entry.Type) { + case SecProfSummary: + if (std::error_code EC = readSummary()) + return EC; + break; + case SecNameTable: + if (std::error_code EC = readNameTable()) + return EC; + break; + default: + continue; + } + if (Data != BufStart + Entry.Offset + Entry.Size) + return sampleprof_error::malformed; + } + + return sampleprof_error::success; +} + +std::error_code SampleProfileReaderBinary::readMagicIdent() { // Read and check the magic identifier. auto Magic = readNumber(); if (std::error_code EC = Magic.getError()) @@ -555,6 +675,16 @@ else if (*Version != SPVersion()) return sampleprof_error::unsupported_version; + return sampleprof_error::success; +} + +std::error_code SampleProfileReaderBinary::readHeader() { + Data = reinterpret_cast(Buffer->getBufferStart()); + End = Data + Buffer->getBufferSize(); + + if (std::error_code EC = readMagicIdent()) + return EC; + if (std::error_code EC = readSummary()) return EC; @@ -674,6 +804,13 @@ return Magic == SPMagic(); } +bool SampleProfileReaderExtBinary::hasFormat(const MemoryBuffer &Buffer) { + const uint8_t *Data = + reinterpret_cast(Buffer.getBufferStart()); + uint64_t Magic = decodeULEB128(Data); + return Magic == SPMagic(SPF_Ext_Binary); +} + bool SampleProfileReaderCompactBinary::hasFormat(const MemoryBuffer &Buffer) { const uint8_t *Data = reinterpret_cast(Buffer.getBufferStart()); @@ -1023,6 +1160,8 @@ std::unique_ptr Reader; if (SampleProfileReaderRawBinary::hasFormat(*B)) Reader.reset(new SampleProfileReaderRawBinary(std::move(B), C)); + else if (SampleProfileReaderExtBinary::hasFormat(*B)) + Reader.reset(new SampleProfileReaderExtBinary(std::move(B), C)); else if (SampleProfileReaderCompactBinary::hasFormat(*B)) Reader.reset(new SampleProfileReaderCompactBinary(std::move(B), C)); else if (SampleProfileReaderGCC::hasFormat(*B)) @@ -1033,8 +1172,9 @@ return sampleprof_error::unrecognized_format; FunctionSamples::Format = Reader->getFormat(); - if (std::error_code EC = Reader->readHeader()) + if (std::error_code EC = Reader->readHeader()) { return EC; + } return std::move(Reader); } Index: lib/ProfileData/SampleProfWriter.cpp =================================================================== --- lib/ProfileData/SampleProfWriter.cpp +++ lib/ProfileData/SampleProfWriter.cpp @@ -39,11 +39,8 @@ using namespace llvm; using namespace sampleprof; -std::error_code -SampleProfileWriter::write(const StringMap &ProfileMap) { - if (std::error_code EC = writeHeader(ProfileMap)) - return EC; - +std::error_code SampleProfileWriter::writeFuncProfiles( + const StringMap &ProfileMap) { // Sort the ProfileMap by total samples. typedef std::pair NameFunctionSamples; std::vector V; @@ -58,12 +55,49 @@ }); for (const auto &I : V) { - if (std::error_code EC = write(*I.second)) + if (std::error_code EC = writeSample(*I.second)) return EC; } return sampleprof_error::success; } +std::error_code +SampleProfileWriter::write(const StringMap &ProfileMap) { + if (std::error_code EC = writeHeader(ProfileMap)) + return EC; + + if (std::error_code EC = writeFuncProfiles(ProfileMap)) + return EC; + + return sampleprof_error::success; +} + +std::error_code SampleProfileWriterExtBinary::write( + const StringMap &ProfileMap) { + if (std::error_code EC = writeHeader(ProfileMap)) + return EC; + + auto &OS = *OutputStream; + uint64_t LastSecEnd = OS.tell(); + if (std::error_code EC = writeFuncProfiles(ProfileMap)) + return EC; + SecHdrTable.push_back( + {SecFuncProfile, 0, LastSecEnd - FileStart, OS.tell() - LastSecEnd}); + + if (SymWhiteList) { + LastSecEnd = OS.tell(); + if (std::error_code EC = SymWhiteList->write(OS)) + return EC; + SecHdrTable.push_back({SecSymbolWhiteList, 0, LastSecEnd - FileStart, + OS.tell() - LastSecEnd}); + } + + if (std::error_code EC = writeSecHdrTable()) + return EC; + + return sampleprof_error::success; +} + std::error_code SampleProfileWriterCompactBinary::write( const StringMap &ProfileMap) { if (std::error_code EC = SampleProfileWriter::write(ProfileMap)) @@ -81,7 +115,7 @@ /// /// The format used here is more structured and deliberate because /// it needs to be parsed by the SampleProfileReaderText class. -std::error_code SampleProfileWriterText::write(const FunctionSamples &S) { +std::error_code SampleProfileWriterText::writeSample(const FunctionSamples &S) { auto &OS = *OutputStream; OS << S.getName() << ":" << S.getTotalSamples(); if (Indent == 0) @@ -117,7 +151,7 @@ OS << Loc.LineOffset << ": "; else OS << Loc.LineOffset << "." << Loc.Discriminator << ": "; - if (std::error_code EC = write(CalleeSamples)) + if (std::error_code EC = writeSample(CalleeSamples)) return EC; } Indent -= 1; @@ -214,29 +248,89 @@ return sampleprof_error::success; } -std::error_code SampleProfileWriterRawBinary::writeMagicIdent() { +std::error_code +SampleProfileWriterBinary::writeMagicIdent(SampleProfileFormat Format) { auto &OS = *OutputStream; // Write file magic identifier. - encodeULEB128(SPMagic(), OS); + encodeULEB128(SPMagic(Format), OS); encodeULEB128(SPVersion(), OS); return sampleprof_error::success; } -std::error_code SampleProfileWriterCompactBinary::writeMagicIdent() { - auto &OS = *OutputStream; - // Write file magic identifier. - encodeULEB128(SPMagic(SPF_Compact_Binary), OS); - encodeULEB128(SPVersion(), OS); +std::error_code SampleProfileWriterBinary::writeHeader( + const StringMap &ProfileMap) { + writeMagicIdent(Format); + + computeSummary(ProfileMap); + if (auto EC = writeSummary()) + return EC; + + // Generate the name table for all the functions referenced in the profile. + for (const auto &I : ProfileMap) { + addName(I.first()); + addNames(I.second); + } + + writeNameTable(); return sampleprof_error::success; } -std::error_code SampleProfileWriterBinary::writeHeader( +SecType SecTable[] = {SecProfSummary, SecNameTable, SecFuncProfile, + SecSymbolWhiteList}; + +void SampleProfileWriterExtBinary::allocSecHdrTable() { + support::endian::Writer Writer(*OutputStream, support::little); + + uint32_t SecNumber = sizeof(SecTable) / sizeof(SecType); + Writer.write(static_cast(SecNumber)); + SecHdrTableOffset = OutputStream->tell(); + for (uint32_t i = 0; i < SecNumber; i++) { + Writer.write(static_cast(-1)); + Writer.write(static_cast(-1)); + Writer.write(static_cast(-1)); + Writer.write(static_cast(-1)); + } +} + +std::error_code SampleProfileWriterExtBinary::writeSecHdrTable() { + auto &OFS = static_cast(*OutputStream); + uint64_t Saved = OutputStream->tell(); + + // Set OutputStream to the location saved in SecHdrTableOffset. + if (OFS.seek(SecHdrTableOffset) == (uint64_t)-1) + return sampleprof_error::ostream_seek_unsupported; + support::endian::Writer Writer(*OutputStream, support::little); + + for (uint32_t i = 0; i < SecHdrTable.size(); i++) { + Writer.write(static_cast(SecHdrTable[i].Type)); + Writer.write(static_cast(SecHdrTable[i].Flag)); + Writer.write(static_cast(SecHdrTable[i].Offset)); + Writer.write(static_cast(SecHdrTable[i].Size)); + } + + // Reset OutputStream. + if (OFS.seek(Saved) == (uint64_t)-1) + return sampleprof_error::ostream_seek_unsupported; + + return sampleprof_error::success; +} + +std::error_code SampleProfileWriterExtBinary::writeHeader( const StringMap &ProfileMap) { - writeMagicIdent(); + auto &OS = *OutputStream; + FileStart = OS.tell(); + writeMagicIdent(Format); + + allocSecHdrTable(); + uint64_t LastSecEnd = OS.tell(); computeSummary(ProfileMap); if (auto EC = writeSummary()) return EC; + uint64_t SecEnd = OS.tell(); + SecHdrTable.push_back( + {SecProfSummary, 0, LastSecEnd - FileStart, SecEnd - LastSecEnd}); + LastSecEnd = SecEnd; // Generate the name table for all the functions referenced in the profile. for (const auto &I : ProfileMap) { @@ -245,6 +339,9 @@ } writeNameTable(); + SecEnd = OS.tell(); + SecHdrTable.push_back( + {SecNameTable, 0, LastSecEnd - FileStart, SecEnd - LastSecEnd}); return sampleprof_error::success; } @@ -324,13 +421,14 @@ /// Write samples of a top-level function to a binary file. /// /// \returns true if the samples were written successfully, false otherwise. -std::error_code SampleProfileWriterBinary::write(const FunctionSamples &S) { +std::error_code +SampleProfileWriterBinary::writeSample(const FunctionSamples &S) { encodeULEB128(S.getHeadSamples(), *OutputStream); return writeBody(S); } std::error_code -SampleProfileWriterCompactBinary::write(const FunctionSamples &S) { +SampleProfileWriterCompactBinary::writeSample(const FunctionSamples &S) { uint64_t Offset = OutputStream->tell(); StringRef Name = S.getName(); FuncOffsetTable[Name] = Offset; @@ -349,7 +447,8 @@ SampleProfileWriter::create(StringRef Filename, SampleProfileFormat Format) { std::error_code EC; std::unique_ptr OS; - if (Format == SPF_Binary || Format == SPF_Compact_Binary) + if (Format == SPF_Binary || Format == SPF_Ext_Binary || + Format == SPF_Compact_Binary) OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_None)); else OS.reset(new raw_fd_ostream(Filename, EC, sys::fs::OF_Text)); @@ -374,6 +473,8 @@ if (Format == SPF_Binary) Writer.reset(new SampleProfileWriterRawBinary(OS)); + else if (Format == SPF_Ext_Binary) + Writer.reset(new SampleProfileWriterExtBinary(OS)); else if (Format == SPF_Compact_Binary) Writer.reset(new SampleProfileWriterCompactBinary(OS)); else if (Format == SPF_Text) @@ -386,6 +487,7 @@ if (EC) return EC; + Writer->Format = Format; return std::move(Writer); } Index: lib/Transforms/IPO/SampleProfile.cpp =================================================================== --- lib/Transforms/IPO/SampleProfile.cpp +++ lib/Transforms/IPO/SampleProfile.cpp @@ -379,6 +379,10 @@ /// Profile Summary Info computed from sample profile. ProfileSummaryInfo *PSI = nullptr; + /// Symbol whitelist tells whether a function name appears in the binary + /// used to generate the current profile. + std::unique_ptr SWL; + /// Total number of samples collected in this profile. /// /// This is the sum of all the samples collected in all the functions executed @@ -1639,6 +1643,7 @@ Reader = std::move(ReaderOrErr.get()); Reader->collectFuncsToUse(M); ProfileIsValid = (Reader->read() == sampleprof_error::success); + SWL = Reader->getSymbolWhiteList(); if (!RemappingFilename.empty()) { // Apply profile remappings to the loaded profile data if requested. @@ -1724,17 +1729,20 @@ } bool SampleProfileLoader::runOnFunction(Function &F, ModuleAnalysisManager *AM) { - + DILocation2SampleMap.clear(); // By default the entry count is initialized to -1, which will be treated // conservatively by getEntryCount as the same as unknown (None). This is // to avoid newly added code to be treated as cold. If we have samples // this will be overwritten in emitAnnotations. // If ProfileSampleAccurate is true or F has profile-sample-accurate - // attribute, initialize the entry count to 0 so callsites or functions - // unsampled will be treated as cold. + // attribute, and if there is no symbol whitelist read in, initialize all + // the function entry counts to 0; if there is symbol whitelist, only + // initialize the entry count to 0 when current function is in the + // whitelist. uint64_t initialEntryCount = - (ProfileSampleAccurate || F.hasFnAttribute("profile-sample-accurate")) + ((ProfileSampleAccurate || F.hasFnAttribute("profile-sample-accurate")) && + (!SWL || SWL->contains(F.getName()))) ? 0 : -1; F.setEntryCount(ProfileCount(initialEntryCount, Function::PCT_Real)); Index: test/Transforms/SampleProfile/compact-binary-profile.ll =================================================================== --- test/Transforms/SampleProfile/compact-binary-profile.ll +++ test/Transforms/SampleProfile/compact-binary-profile.ll @@ -1,121 +0,0 @@ -; RUN: opt < %s -sample-profile -sample-profile-file=%S/Inputs/inline.prof -S | FileCheck %s -; RUN: opt < %s -passes=sample-profile -sample-profile-file=%S/Inputs/inline.prof -S | FileCheck %s -; RUN: opt < %s -sample-profile -sample-profile-file=%S/Inputs/inline.compactbinary.afdo -S | FileCheck %s -; RUN: opt < %s -passes=sample-profile -sample-profile-file=%S/Inputs/inline.compactbinary.afdo -S | FileCheck %s - -; Original C++ test case -; -; #include -; -; int sum(int x, int y) { -; return x + y; -; } -; -; int main() { -; int s, i = 0; -; while (i++ < 20000 * 20000) -; if (i != 100) s = sum(i, s); else s = 30; -; printf("sum is %d\n", s); -; return 0; -; } -; -@.str = private unnamed_addr constant [11 x i8] c"sum is %d\0A\00", align 1 - -; Check sample-profile phase using compactbinary format profile will annotate -; the IR with exactly the same result as using text format. -; CHECK: br i1 %cmp, label %while.body, label %while.end{{.*}} !prof ![[IDX1:[0-9]*]] -; CHECK: br i1 %cmp1, label %if.then, label %if.else{{.*}} !prof ![[IDX2:[0-9]*]] -; CHECK: call i32 (i8*, ...) @printf{{.*}} !prof ![[IDX3:[0-9]*]] -; CHECK: = !{!"TotalCount", i64 26781} -; CHECK: = !{!"MaxCount", i64 5553} -; CHECK: ![[IDX1]] = !{!"branch_weights", i32 5392, i32 163} -; CHECK: ![[IDX2]] = !{!"branch_weights", i32 5280, i32 113} -; CHECK: ![[IDX3]] = !{!"branch_weights", i32 1} - -; Function Attrs: nounwind uwtable -define i32 @_Z3sumii(i32 %x, i32 %y) !dbg !4 { -entry: - %x.addr = alloca i32, align 4 - %y.addr = alloca i32, align 4 - store i32 %x, i32* %x.addr, align 4 - store i32 %y, i32* %y.addr, align 4 - %0 = load i32, i32* %x.addr, align 4, !dbg !11 - %1 = load i32, i32* %y.addr, align 4, !dbg !11 - %add = add nsw i32 %0, %1, !dbg !11 - ret i32 %add, !dbg !11 -} - -; Function Attrs: uwtable -define i32 @main() !dbg !7 { -entry: - %retval = alloca i32, align 4 - %s = alloca i32, align 4 - %i = alloca i32, align 4 - store i32 0, i32* %retval - store i32 0, i32* %i, align 4, !dbg !12 - br label %while.cond, !dbg !13 - -while.cond: ; preds = %if.end, %entry - %0 = load i32, i32* %i, align 4, !dbg !14 - %inc = add nsw i32 %0, 1, !dbg !14 - store i32 %inc, i32* %i, align 4, !dbg !14 - %cmp = icmp slt i32 %0, 400000000, !dbg !14 - br i1 %cmp, label %while.body, label %while.end, !dbg !14 - -while.body: ; preds = %while.cond - %1 = load i32, i32* %i, align 4, !dbg !16 - %cmp1 = icmp ne i32 %1, 100, !dbg !16 - br i1 %cmp1, label %if.then, label %if.else, !dbg !16 - - -if.then: ; preds = %while.body - %2 = load i32, i32* %i, align 4, !dbg !18 - %3 = load i32, i32* %s, align 4, !dbg !18 - %call = call i32 @_Z3sumii(i32 %2, i32 %3), !dbg !18 - store i32 %call, i32* %s, align 4, !dbg !18 - br label %if.end, !dbg !18 - -if.else: ; preds = %while.body - store i32 30, i32* %s, align 4, !dbg !20 - br label %if.end - -if.end: ; preds = %if.else, %if.then - br label %while.cond, !dbg !22 - -while.end: ; preds = %while.cond - %4 = load i32, i32* %s, align 4, !dbg !24 - %call2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str, i32 0, i32 0), i32 %4), !dbg !24 - ret i32 0, !dbg !25 -} - -declare i32 @printf(i8*, ...) #2 - -!llvm.dbg.cu = !{!0} -!llvm.module.flags = !{!8, !9} -!llvm.ident = !{!10} - -!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang version 3.5 ", isOptimized: false, emissionKind: NoDebug, file: !1, enums: !2, retainedTypes: !2, globals: !2, imports: !2) -!1 = !DIFile(filename: "calls.cc", directory: ".") -!2 = !{} -!4 = distinct !DISubprogram(name: "sum", line: 3, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, unit: !0, scopeLine: 3, file: !1, scope: !5, type: !6, retainedNodes: !2) -!5 = !DIFile(filename: "calls.cc", directory: ".") -!6 = !DISubroutineType(types: !2) -!7 = distinct !DISubprogram(name: "main", line: 7, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, unit: !0, scopeLine: 7, file: !1, scope: !5, type: !6, retainedNodes: !2) -!8 = !{i32 2, !"Dwarf Version", i32 4} -!9 = !{i32 1, !"Debug Info Version", i32 3} -!10 = !{!"clang version 3.5 "} -!11 = !DILocation(line: 4, scope: !4) -!12 = !DILocation(line: 8, scope: !7) -!13 = !DILocation(line: 9, scope: !7) -!14 = !DILocation(line: 9, scope: !15) -!15 = !DILexicalBlockFile(discriminator: 2, file: !1, scope: !7) -!16 = !DILocation(line: 10, scope: !17) -!17 = distinct !DILexicalBlock(line: 10, column: 0, file: !1, scope: !7) -!18 = !DILocation(line: 10, scope: !19) -!19 = !DILexicalBlockFile(discriminator: 2, file: !1, scope: !17) -!20 = !DILocation(line: 10, scope: !21) -!21 = !DILexicalBlockFile(discriminator: 4, file: !1, scope: !17) -!22 = !DILocation(line: 10, scope: !23) -!23 = !DILexicalBlockFile(discriminator: 6, file: !1, scope: !17) -!24 = !DILocation(line: 11, scope: !7) -!25 = !DILocation(line: 12, scope: !7) Index: test/Transforms/SampleProfile/profile-format.ll =================================================================== --- test/Transforms/SampleProfile/profile-format.ll +++ test/Transforms/SampleProfile/profile-format.ll @@ -0,0 +1,123 @@ +; RUN: opt < %s -sample-profile -sample-profile-file=%S/Inputs/inline.prof -S | FileCheck %s +; RUN: opt < %s -passes=sample-profile -sample-profile-file=%S/Inputs/inline.prof -S | FileCheck %s +; RUN: opt < %s -sample-profile -sample-profile-file=%S/Inputs/inline.compactbinary.afdo -S | FileCheck %s +; RUN: opt < %s -passes=sample-profile -sample-profile-file=%S/Inputs/inline.compactbinary.afdo -S | FileCheck %s +; RUN: opt < %s -sample-profile -sample-profile-file=%S/Inputs/inline.extbinary.afdo -S | FileCheck %s +; RUN: opt < %s -passes=sample-profile -sample-profile-file=%S/Inputs/inline.extbinary.afdo -S | FileCheck %s + +; Original C++ test case +; +; #include +; +; int sum(int x, int y) { +; return x + y; +; } +; +; int main() { +; int s, i = 0; +; while (i++ < 20000 * 20000) +; if (i != 100) s = sum(i, s); else s = 30; +; printf("sum is %d\n", s); +; return 0; +; } +; +@.str = private unnamed_addr constant [11 x i8] c"sum is %d\0A\00", align 1 + +; Check sample-profile phase using compactbinary or extbinary format profile +; will annotate the IR with exactly the same result as using text format. +; CHECK: br i1 %cmp, label %while.body, label %while.end{{.*}} !prof ![[IDX1:[0-9]*]] +; CHECK: br i1 %cmp1, label %if.then, label %if.else{{.*}} !prof ![[IDX2:[0-9]*]] +; CHECK: call i32 (i8*, ...) @printf{{.*}} !prof ![[IDX3:[0-9]*]] +; CHECK: = !{!"TotalCount", i64 26781} +; CHECK: = !{!"MaxCount", i64 5553} +; CHECK: ![[IDX1]] = !{!"branch_weights", i32 5392, i32 163} +; CHECK: ![[IDX2]] = !{!"branch_weights", i32 5280, i32 113} +; CHECK: ![[IDX3]] = !{!"branch_weights", i32 1} + +; Function Attrs: nounwind uwtable +define i32 @_Z3sumii(i32 %x, i32 %y) !dbg !4 { +entry: + %x.addr = alloca i32, align 4 + %y.addr = alloca i32, align 4 + store i32 %x, i32* %x.addr, align 4 + store i32 %y, i32* %y.addr, align 4 + %0 = load i32, i32* %x.addr, align 4, !dbg !11 + %1 = load i32, i32* %y.addr, align 4, !dbg !11 + %add = add nsw i32 %0, %1, !dbg !11 + ret i32 %add, !dbg !11 +} + +; Function Attrs: uwtable +define i32 @main() !dbg !7 { +entry: + %retval = alloca i32, align 4 + %s = alloca i32, align 4 + %i = alloca i32, align 4 + store i32 0, i32* %retval + store i32 0, i32* %i, align 4, !dbg !12 + br label %while.cond, !dbg !13 + +while.cond: ; preds = %if.end, %entry + %0 = load i32, i32* %i, align 4, !dbg !14 + %inc = add nsw i32 %0, 1, !dbg !14 + store i32 %inc, i32* %i, align 4, !dbg !14 + %cmp = icmp slt i32 %0, 400000000, !dbg !14 + br i1 %cmp, label %while.body, label %while.end, !dbg !14 + +while.body: ; preds = %while.cond + %1 = load i32, i32* %i, align 4, !dbg !16 + %cmp1 = icmp ne i32 %1, 100, !dbg !16 + br i1 %cmp1, label %if.then, label %if.else, !dbg !16 + + +if.then: ; preds = %while.body + %2 = load i32, i32* %i, align 4, !dbg !18 + %3 = load i32, i32* %s, align 4, !dbg !18 + %call = call i32 @_Z3sumii(i32 %2, i32 %3), !dbg !18 + store i32 %call, i32* %s, align 4, !dbg !18 + br label %if.end, !dbg !18 + +if.else: ; preds = %while.body + store i32 30, i32* %s, align 4, !dbg !20 + br label %if.end + +if.end: ; preds = %if.else, %if.then + br label %while.cond, !dbg !22 + +while.end: ; preds = %while.cond + %4 = load i32, i32* %s, align 4, !dbg !24 + %call2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str, i32 0, i32 0), i32 %4), !dbg !24 + ret i32 0, !dbg !25 +} + +declare i32 @printf(i8*, ...) #2 + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!8, !9} +!llvm.ident = !{!10} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang version 3.5 ", isOptimized: false, emissionKind: NoDebug, file: !1, enums: !2, retainedTypes: !2, globals: !2, imports: !2) +!1 = !DIFile(filename: "calls.cc", directory: ".") +!2 = !{} +!4 = distinct !DISubprogram(name: "sum", line: 3, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, unit: !0, scopeLine: 3, file: !1, scope: !5, type: !6, retainedNodes: !2) +!5 = !DIFile(filename: "calls.cc", directory: ".") +!6 = !DISubroutineType(types: !2) +!7 = distinct !DISubprogram(name: "main", line: 7, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, unit: !0, scopeLine: 7, file: !1, scope: !5, type: !6, retainedNodes: !2) +!8 = !{i32 2, !"Dwarf Version", i32 4} +!9 = !{i32 1, !"Debug Info Version", i32 3} +!10 = !{!"clang version 3.5 "} +!11 = !DILocation(line: 4, scope: !4) +!12 = !DILocation(line: 8, scope: !7) +!13 = !DILocation(line: 9, scope: !7) +!14 = !DILocation(line: 9, scope: !15) +!15 = !DILexicalBlockFile(discriminator: 2, file: !1, scope: !7) +!16 = !DILocation(line: 10, scope: !17) +!17 = distinct !DILexicalBlock(line: 10, column: 0, file: !1, scope: !7) +!18 = !DILocation(line: 10, scope: !19) +!19 = !DILexicalBlockFile(discriminator: 2, file: !1, scope: !17) +!20 = !DILocation(line: 10, scope: !21) +!21 = !DILexicalBlockFile(discriminator: 4, file: !1, scope: !17) +!22 = !DILocation(line: 10, scope: !23) +!23 = !DILexicalBlockFile(discriminator: 6, file: !1, scope: !17) +!24 = !DILocation(line: 11, scope: !7) +!25 = !DILocation(line: 12, scope: !7) Index: test/Transforms/SampleProfile/symbol-white-list.ll =================================================================== --- test/Transforms/SampleProfile/symbol-white-list.ll +++ test/Transforms/SampleProfile/symbol-white-list.ll @@ -0,0 +1,137 @@ +; RUN: opt < %s -sample-profile -profile-sample-accurate -sample-profile-file=%S/Inputs/inline.extbinary.afdo -S | FileCheck %s +; RUN: opt < %s -passes=sample-profile -profile-sample-accurate -sample-profile-file=%S/Inputs/inline.extbinary.afdo -S | FileCheck %s + +; Original C++ test case +; +; #include +; +; __attribute__((noinline)) int goo() { return 3 }; +; __attribute__((noinline)) int hoo() { return 4 }; +; +; int sum(int x, int y) { +; return x + y; +; } +; +; int main() { +; int s, i = 0; +; while (i++ < 20000 * 20000) +; if (i != 100) s = sum(i, s); else s = 30; +; printf("sum is %d\n", s); +; return goo() + hoo() != 7; +; } +; +; Both goo and hoo don't show up in the input profile. +; Suppose function goo shows up in the binary generating the input profile +; and function hoo doesn't show up. Then the symbol whitelist in the input +; profile will contain goo but not hoo. Verify the entry count of goo is +; 0 and the entry count of hoo is -1. +; CHECK: define {{.*}} i32 @_Z3goov() {{.*}} !prof ![[IDX1:[0-9]*]] +; CHECK: define {{.*}} i32 @_Z3hoov() {{.*}} !prof ![[IDX2:[0-9]*]] +; CHECK: ![[IDX1]] = !{!"function_entry_count", i64 0} +; CHECK: ![[IDX2]] = !{!"function_entry_count", i64 -1} + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@.str = private unnamed_addr constant [11 x i8] c"sum is %d\0A\00", align 1 + +; Function Attrs: noinline norecurse nounwind readnone uwtable +define dso_local i32 @_Z3goov() local_unnamed_addr #0 !dbg !7 { +entry: + ret i32 3, !dbg !9 +} + +; Function Attrs: noinline norecurse nounwind readnone uwtable +define dso_local i32 @_Z3hoov() local_unnamed_addr #0 !dbg !10 { +entry: + ret i32 4, !dbg !11 +} + +; Function Attrs: norecurse nounwind readnone uwtable +define dso_local i32 @_Z3sumii(i32 %x, i32 %y) local_unnamed_addr #1 !dbg !12 { +entry: + %add = add nsw i32 %y, %x, !dbg !13 + ret i32 %add, !dbg !14 +} + +; Function Attrs: nofree norecurse nounwind uwtable +define dso_local i32 @main() local_unnamed_addr #2 !dbg !15 { +entry: + br label %while.body, !dbg !16 + +while.body: ; preds = %while.body, %entry + %inc12 = phi i32 [ 1, %entry ], [ %inc.4, %while.body ] + %s.011 = phi i32 [ undef, %entry ], [ %spec.select.4, %while.body ] + %cmp1 = icmp eq i32 %inc12, 100, !dbg !18 + %add.i = add nsw i32 %inc12, %s.011, !dbg !20 + %spec.select = select i1 %cmp1, i32 30, i32 %add.i, !dbg !23 + %inc = add nuw nsw i32 %inc12, 1, !dbg !24 + %cmp1.1 = icmp eq i32 %inc, 100, !dbg !18 + %add.i.1 = add nsw i32 %inc, %spec.select, !dbg !20 + %spec.select.1 = select i1 %cmp1.1, i32 30, i32 %add.i.1, !dbg !23 + %inc.1 = add nuw nsw i32 %inc12, 2, !dbg !24 + %cmp1.2 = icmp eq i32 %inc.1, 100, !dbg !18 + %add.i.2 = add nsw i32 %inc.1, %spec.select.1, !dbg !20 + %spec.select.2 = select i1 %cmp1.2, i32 30, i32 %add.i.2, !dbg !23 + %inc.2 = add nuw nsw i32 %inc12, 3, !dbg !24 + %cmp1.3 = icmp eq i32 %inc.2, 100, !dbg !18 + %add.i.3 = add nsw i32 %inc.2, %spec.select.2, !dbg !20 + %spec.select.3 = select i1 %cmp1.3, i32 30, i32 %add.i.3, !dbg !23 + %inc.3 = add nuw nsw i32 %inc12, 4, !dbg !24 + %cmp1.4 = icmp eq i32 %inc.3, 100, !dbg !18 + %add.i.4 = add nsw i32 %inc.3, %spec.select.3, !dbg !20 + %spec.select.4 = select i1 %cmp1.4, i32 30, i32 %add.i.4, !dbg !23 + %inc.4 = add nuw nsw i32 %inc12, 5, !dbg !24 + %exitcond.4 = icmp eq i32 %inc.4, 400000001, !dbg !26 + br i1 %exitcond.4, label %while.end, label %while.body, !dbg !27, !llvm.loop !28 + +while.end: ; preds = %while.body + %call2 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str, i64 0, i64 0), i32 %spec.select.4), !dbg !31 + ret i32 0, !dbg !32 +} + +; Function Attrs: nofree nounwind +declare dso_local i32 @printf(i8* nocapture readonly, ...) local_unnamed_addr #3 + +attributes #0 = { noinline norecurse nounwind readnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { norecurse nounwind readnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nofree norecurse nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { nofree nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 10.0.0 (trunk 369144)", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, enums: !2, debugInfoForProfiling: true, nameTableKind: None) +!1 = !DIFile(filename: "1.cc", directory: "/usr/local/google/home/wmi/workarea/llvm-r369144/src") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 10.0.0 (trunk 369144)"} +!7 = distinct !DISubprogram(name: "goo", linkageName: "_Z3goov", scope: !1, file: !1, line: 3, type: !8, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2) +!8 = !DISubroutineType(types: !2) +!9 = !DILocation(line: 3, column: 39, scope: !7) +!10 = distinct !DISubprogram(name: "hoo", linkageName: "_Z3hoov", scope: !1, file: !1, line: 4, type: !8, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2) +!11 = !DILocation(line: 4, column: 39, scope: !10) +!12 = distinct !DISubprogram(name: "sum", linkageName: "_Z3sumii", scope: !1, file: !1, line: 6, type: !8, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2) +!13 = !DILocation(line: 7, column: 12, scope: !12) +!14 = !DILocation(line: 7, column: 3, scope: !12) +!15 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 10, type: !8, scopeLine: 10, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2) +!16 = !DILocation(line: 12, column: 3, scope: !17) +!17 = !DILexicalBlockFile(scope: !15, file: !1, discriminator: 2) +!18 = !DILocation(line: 13, column: 11, scope: !19) +!19 = !DILexicalBlockFile(scope: !15, file: !1, discriminator: 21) +!20 = !DILocation(line: 7, column: 12, scope: !21, inlinedAt: !22) +!21 = !DILexicalBlockFile(scope: !12, file: !1, discriminator: 21) +!22 = distinct !DILocation(line: 13, column: 23, scope: !17) +!23 = !DILocation(line: 13, column: 9, scope: !19) +!24 = !DILocation(line: 12, column: 11, scope: !25) +!25 = !DILexicalBlockFile(scope: !15, file: !1, discriminator: 1282) +!26 = !DILocation(line: 12, column: 14, scope: !25) +!27 = !DILocation(line: 12, column: 3, scope: !25) +!28 = distinct !{!28, !29, !30} +!29 = !DILocation(line: 12, column: 3, scope: !15) +!30 = !DILocation(line: 13, column: 43, scope: !15) +!31 = !DILocation(line: 14, column: 3, scope: !15) +!32 = !DILocation(line: 15, column: 3, scope: !15) Index: tools/llvm-profdata/llvm-profdata.cpp =================================================================== --- tools/llvm-profdata/llvm-profdata.cpp +++ tools/llvm-profdata/llvm-profdata.cpp @@ -37,6 +37,7 @@ PF_None = 0, PF_Text, PF_Compact_Binary, + PF_Ext_Binary, PF_GCC, PF_Binary }; @@ -314,7 +315,7 @@ exitWithError("Cannot write indexed profdata format to stdout."); if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary && - OutputFormat != PF_Text) + OutputFormat != PF_Ext_Binary && OutputFormat != PF_Text) exitWithError("Unknown format is specified."); std::mutex ErrorLock; @@ -425,8 +426,12 @@ } static sampleprof::SampleProfileFormat FormatMap[] = { - sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Compact_Binary, - sampleprof::SPF_GCC, sampleprof::SPF_Binary}; + sampleprof::SPF_None, + sampleprof::SPF_Text, + sampleprof::SPF_Compact_Binary, + sampleprof::SPF_Ext_Binary, + sampleprof::SPF_GCC, + sampleprof::SPF_Binary}; static void mergeSampleProfile(const WeightedFileVector &Inputs, SymbolRemapper *Remapper, @@ -436,6 +441,7 @@ StringMap ProfileMap; SmallVector, 5> Readers; LLVMContext Context; + sampleprof::SymbolWhiteList WriterList; for (const auto &Input : Inputs) { auto ReaderOrErr = SampleProfileReader::create(Input.Filename, Context); if (std::error_code EC = ReaderOrErr.getError()) @@ -466,6 +472,11 @@ handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName); } } + + std::unique_ptr ReaderList = + Reader->getSymbolWhiteList(); + if (ReaderList) + WriterList.merge(*ReaderList); } auto WriterOrErr = SampleProfileWriter::create(OutputFilename, FormatMap[OutputFormat]); @@ -473,6 +484,7 @@ exitWithErrorCode(EC, OutputFilename); auto Writer = std::move(WriterOrErr.get()); + Writer->setSymbolWhiteList(&WriterList); Writer->write(ProfileMap); } Index: unittests/ProfileData/SampleProfTest.cpp =================================================================== --- unittests/ProfileData/SampleProfTest.cpp +++ unittests/ProfileData/SampleProfTest.cpp @@ -96,6 +96,13 @@ Profiles[FooName] = std::move(FooSamples); Profiles[BarName] = std::move(BarSamples); + SymbolWhiteList List; + if (Format == SampleProfileFormat::SPF_Ext_Binary) { + List.add("zoo", true); + List.add("moo", true); + } + Writer->setSymbolWhiteList(&List); + std::error_code EC; EC = Writer->write(Profiles); ASSERT_TRUE(NoError(EC)); @@ -107,6 +114,13 @@ EC = Reader->read(); ASSERT_TRUE(NoError(EC)); + if (Format == SampleProfileFormat::SPF_Ext_Binary) { + std::unique_ptr ReaderList = + Reader->getSymbolWhiteList(); + ReaderList->contains("zoo"); + ReaderList->contains("moo"); + } + if (Remap) { auto MemBuffer = llvm::MemoryBuffer::getMemBuffer(R"( # Types 'int' and 'long' are equivalent @@ -285,6 +299,10 @@ testRoundTrip(SampleProfileFormat::SPF_Compact_Binary, false); } +TEST_F(SampleProfTest, roundtrip_ext_binary_profile) { + testRoundTrip(SampleProfileFormat::SPF_Ext_Binary, false); +} + TEST_F(SampleProfTest, remap_text_profile) { testRoundTrip(SampleProfileFormat::SPF_Text, true); } @@ -293,6 +311,10 @@ testRoundTrip(SampleProfileFormat::SPF_Binary, true); } +TEST_F(SampleProfTest, remap_ext_binary_profile) { + testRoundTrip(SampleProfileFormat::SPF_Ext_Binary, true); +} + TEST_F(SampleProfTest, sample_overflow_saturation) { const uint64_t Max = std::numeric_limits::max(); sampleprof_error Result;