From: Xinliang David Li Date: Tue, 10 Nov 2015 00:24:45 +0000 (+0000) Subject: [PGO] Make indexed value profile data more compact X-Git-Url: http://plrg.eecs.uci.edu/git/?p=oota-llvm.git;a=commitdiff_plain;h=bcd8e0aae7dc4eea0ff0a425ba164db10c4dcb71 [PGO] Make indexed value profile data more compact - Make indexed value profile data more compact by peeling out the per-site value count field into its own smaller sized array. - Introduced formal data structure definitions to specify value profile data layout in indexed format. Previously the layout of the data is only assumed in the client code (scattered in three different places : size computation, EmitData, and ReadData - The new data structure serves as a central place for layout documentation. - Add interfaces to force BE output for value profile data (testing purpose) - Add byte swap unit tests Differential Revision: http://reviews.llvm.org/D14401 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@252563 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm/ProfileData/InstrProf.h b/include/llvm/ProfileData/InstrProf.h index 0afbc38c374..607f29de8a2 100644 --- a/include/llvm/ProfileData/InstrProf.h +++ b/include/llvm/ProfileData/InstrProf.h @@ -21,6 +21,7 @@ #include "llvm/IR/GlobalValue.h" #include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" #include "llvm/Support/MD5.h" #include #include @@ -318,6 +319,16 @@ uint32_t InstrProfRecord::getNumValueKinds() const { return NumValueKinds; } +uint32_t InstrProfRecord::getNumValueData(uint32_t ValueKind) const { + uint32_t N = 0; + const std::vector &SiteRecords = + getValueSitesForKind(ValueKind); + for (auto &SR : SiteRecords) { + N += SR.ValueData.size(); + } + return N; +} + uint32_t InstrProfRecord::getNumValueSites(uint32_t ValueKind) const { return getValueSitesForKind(ValueKind).size(); } @@ -419,6 +430,8 @@ const uint64_t Magic = 0x8169666f72706cff; // "\xfflprofi\x81" const uint64_t Version = 3; const HashT HashType = HashT::MD5; +// This structure defines the file header of the LLVM profile +// data file in indexed-format. struct Header { uint64_t Magic; uint64_t Version; @@ -427,6 +440,105 @@ struct Header { uint64_t HashOffset; }; +inline support::endianness getHostEndianness() { + return sys::IsLittleEndianHost ? support::little : support::big; +} + +/// This is the header of the data structure that defines the on-disk +/// layout of the value profile data of a particular kind for one function. +struct ValueProfRecord { + // The kind of the value profile record. + uint32_t Kind; + // The number of value profile sites. It is guaranteed to be non-zero; + // otherwise the record for this kind won't be emitted. + uint32_t NumValueSites; + // The first element of the array that stores the number of profiled + // values for each value site. The size of the array is NumValueSites. + // Since NumValueSites is greater than zero, there is at least one + // element in the array. + uint8_t SiteCountArray[1]; + + // The fake declaration is for documentation purpose only. + // Align the start of next field to be on 8 byte boundaries. + // uint8_t Padding[X]; + + // The array of value profile data. The size of the array is the sum + // of all elements in SiteCountArray[]. + // InstrProfValueData ValueData[]; + + /// Return the \c ValueProfRecord header size including the padding bytes. + static uint32_t getHeaderSize(uint32_t NumValueSites); + /// Return the total size of the value profile record including the + /// header and the value data. + static uint32_t getSize(uint32_t NumValueSites, uint32_t NumValueData); + /// Return the total size of the value profile record including the + /// header and the value data. + uint32_t getSize() const { return getSize(NumValueSites, getNumValueData()); } + /// Use this method to advance to the next \c ValueProfRecord. + ValueProfRecord *getNext(); + /// Return the pointer to the first value profile data. + InstrProfValueData *getValueData(); + /// Return the number of value sites. + uint32_t getNumValueSites() const { return NumValueSites; } + /// Return the number of value data. + uint32_t getNumValueData() const; + /// Read data from this record and save it to Record. + void deserializeTo(InstrProfRecord &Record, + InstrProfRecord::ValueMapType *VMap); + /// Extract data from \c Record and serialize into this instance. + void serializeFrom(const InstrProfRecord &Record, uint32_t ValueKind, + uint32_t NumValueSites); + /// In-place byte swap: + /// Do byte swap for this instance. \c Old is the original order before + /// the swap, and \c New is the New byte order. + void swapBytes(support::endianness Old, support::endianness New); +}; + +/// Per-function header/control data structure for value profiling +/// data in indexed format. +struct ValueProfData { + // Total size in bytes including this field. It must be a multiple + // of sizeof(uint64_t). + uint32_t TotalSize; + // The number of value profile kinds that has value profile data. + // In this implementation, a value profile kind is considered to + // have profile data if the number of value profile sites for the + // kind is not zero. More aggressively, the implemnetation can + // choose to check the actual data value: if none of the value sites + // has any profiled values, the kind can be skipped. + uint32_t NumValueKinds; + + // Following are a sequence of variable length records. The prefix/header + // of each record is defined by ValueProfRecord type. The number of + // records is NumValueKinds. + // ValueProfRecord Record_1; + // ValueProfRecord Record_N; + + /// Return the total size in bytes of the on-disk value profile data + /// given the data stored in Record. + static uint32_t getSize(const InstrProfRecord &Record); + /// Return a pointer to \c ValueProfData instance ready to be streamed. + static std::unique_ptr + serializeFrom(const InstrProfRecord &Record); + /// Return a pointer to \c ValueProfileData instance ready to be read. + /// All data in the instance are properly byte swapped. The input + /// data is assumed to be in little endian order. + static ErrorOr> + getValueProfData(const unsigned char *D, const unsigned char *const BufferEnd, + support::endianness SrcDataEndianness); + /// Swap byte order from \c Endianness order to host byte order. + void swapBytesToHost(support::endianness Endianness); + /// Swap byte order from host byte order to \c Endianness order. + void swapBytesFromHost(support::endianness Endianness); + /// Return the total size of \c ValueProfileData. + uint32_t getSize() const { return TotalSize; } + /// Read data from this data and save it to \c Record. + void deserializeTo(InstrProfRecord &Record, + InstrProfRecord::ValueMapType *VMap); + /// Return the first \c ValueProfRecord instance. + ValueProfRecord *getFirstValueProfRecord(); +}; + } // end namespace IndexedInstrProf namespace RawInstrProf { @@ -458,20 +570,20 @@ inline uint64_t getMagic() { uint64_t('R') << 8 | uint64_t(129); } +// Per-function profile data header/control structure. // The definition should match the structure defined in // compiler-rt/lib/profile/InstrProfiling.h. // It should also match the synthesized type in // Transforms/Instrumentation/InstrProfiling.cpp:getOrCreateRegionCounters. - template struct ProfileData { #define INSTR_PROF_DATA(Type, LLVMType, Name, Init) Type Name; #include "llvm/ProfileData/InstrProfData.inc" }; +// File header structure of the LLVM profile data in raw format. // The definition should match the header referenced in // compiler-rt/lib/profile/InstrProfilingFile.c and // InstrProfilingBuffer.c. - struct Header { const uint64_t Magic; const uint64_t Version; @@ -486,6 +598,14 @@ struct Header { namespace coverage { +// Profile coverage map has the following layout: +// [CoverageMapFileHeader] +// [ArrayStart] +// [CovMapFunctionRecord] +// [CovMapFunctionRecord] +// ... +// [ArrayEnd] +// [Encoded Region Mapping Data] LLVM_PACKED_START template struct CovMapFunctionRecord { #define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Type Name; diff --git a/include/llvm/ProfileData/InstrProfReader.h b/include/llvm/ProfileData/InstrProfReader.h index 69e899d806e..d0f5a57b1ae 100644 --- a/include/llvm/ProfileData/InstrProfReader.h +++ b/include/llvm/ProfileData/InstrProfReader.h @@ -186,11 +186,16 @@ class InstrProfLookupTrait { std::vector DataBuffer; IndexedInstrProf::HashT HashType; unsigned FormatVersion; + // Endianness of the input value profile data. + // It should be LE by default, but can be changed + // for testing purpose. + support::endianness ValueProfDataEndianness; std::vector> HashKeys; public: InstrProfLookupTrait(IndexedInstrProf::HashT HashType, unsigned FormatVersion) - : HashType(HashType), FormatVersion(FormatVersion) {} + : HashType(HashType), FormatVersion(FormatVersion), + ValueProfDataEndianness(support::little) {} typedef ArrayRef data_type; @@ -223,6 +228,11 @@ public: bool ReadValueProfilingData(const unsigned char *&D, const unsigned char *const End); data_type ReadData(StringRef K, const unsigned char *D, offset_type N); + + // Used for testing purpose only. + void setValueProfDataEndianness(support::endianness Endianness) { + ValueProfDataEndianness = Endianness; + } }; class InstrProfReaderIndex { @@ -251,6 +261,10 @@ class InstrProfReaderIndex { void advanceToNextKey() { RecordIterator++; } bool atEnd() const { return RecordIterator == Index->data_end(); } + // Used for testing purpose only. + void setValueProfDataEndianness(support::endianness Endianness) { + Index->getInfoObj().setValueProfDataEndianness(Endianness); + } }; /// Reader for the indexed binary instrprof format. @@ -295,6 +309,11 @@ private: static ErrorOr> create(std::unique_ptr Buffer); + + // Used for testing purpose only. + void setValueProfDataEndianness(support::endianness Endianness) { + Index.setValueProfDataEndianness(Endianness); + } }; } // end namespace llvm diff --git a/include/llvm/ProfileData/InstrProfWriter.h b/include/llvm/ProfileData/InstrProfWriter.h index da0d956e8a9..5b122d69977 100644 --- a/include/llvm/ProfileData/InstrProfWriter.h +++ b/include/llvm/ProfileData/InstrProfWriter.h @@ -46,6 +46,9 @@ public: /// Write the profile, returning the raw data. For testing. std::unique_ptr writeBuffer(); + // Internal interface for testing purpose only. + void setValueProfDataEndianness(support::endianness Endianness); + private: std::pair writeImpl(raw_ostream &OS); }; diff --git a/lib/ProfileData/InstrProf.cpp b/lib/ProfileData/InstrProf.cpp index 377d77197d9..22c04fbf58e 100644 --- a/lib/ProfileData/InstrProf.cpp +++ b/lib/ProfileData/InstrProf.cpp @@ -128,4 +128,232 @@ GlobalVariable *createPGOFuncNameVar(Module &M, GlobalVariable *createPGOFuncNameVar(Function &F, StringRef FuncName) { return createPGOFuncNameVar(*F.getParent(), F.getLinkage(), FuncName); } + +namespace IndexedInstrProf { + +uint32_t ValueProfRecord::getHeaderSize(uint32_t NumValueSites) { + uint32_t Size = offsetof(ValueProfRecord, SiteCountArray) + + sizeof(uint8_t) * NumValueSites; + // Round the size to multiple of 8 bytes. + Size = (Size + 7) & ~7; + return Size; +} + +uint32_t ValueProfRecord::getSize(uint32_t NumValueSites, + uint32_t NumValueData) { + return getHeaderSize(NumValueSites) + + sizeof(InstrProfValueData) * NumValueData; +} + +void ValueProfRecord::deserializeTo(InstrProfRecord &Record, + InstrProfRecord::ValueMapType *VMap) { + Record.reserveSites(Kind, NumValueSites); + + InstrProfValueData *ValueData = this->getValueData(); + for (uint64_t VSite = 0; VSite < NumValueSites; ++VSite) { + uint8_t ValueDataCount = this->SiteCountArray[VSite]; + Record.addValueData(Kind, VSite, ValueData, ValueDataCount, VMap); + ValueData += ValueDataCount; + } +} + +void ValueProfRecord::serializeFrom(const InstrProfRecord &Record, + uint32_t ValueKind, + uint32_t NumValueSites) { + Kind = ValueKind; + this->NumValueSites = NumValueSites; + InstrProfValueData *DstVD = getValueData(); + for (uint32_t S = 0; S < NumValueSites; S++) { + uint32_t ND = Record.getNumValueDataForSite(ValueKind, S); + SiteCountArray[S] = ND; + std::unique_ptr SrcVD = + Record.getValueForSite(ValueKind, S); + for (uint32_t I = 0; I < ND; I++) { + DstVD[I] = SrcVD[I]; + switch (ValueKind) { + case IPVK_IndirectCallTarget: + DstVD[I].Value = ComputeHash(HashType, (const char *)DstVD[I].Value); + break; + default: + llvm_unreachable("value kind not handled !"); + } + } + DstVD += ND; + } +} + +template static T swapToHostOrder(T v, support::endianness Orig) { + if (Orig == getHostEndianness()) + return v; + sys::swapByteOrder(v); + return v; +} + +// For writing/serializing, Old is the host endianness, and New is +// byte order intended on disk. For Reading/deserialization, Old +// is the on-disk source endianness, and New is the host endianness. +void ValueProfRecord::swapBytes(support::endianness Old, + support::endianness New) { + using namespace support; + if (Old == New) + return; + + if (getHostEndianness() != Old) { + sys::swapByteOrder(NumValueSites); + sys::swapByteOrder(Kind); + } + uint32_t ND = getNumValueData(); + InstrProfValueData *VD = getValueData(); + + // No need to swap byte array: SiteCountArrray. + for (uint32_t I = 0; I < ND; I++) { + sys::swapByteOrder(VD[I].Value); + sys::swapByteOrder(VD[I].Count); + } + if (getHostEndianness() == Old) { + sys::swapByteOrder(NumValueSites); + sys::swapByteOrder(Kind); + } +} + +uint32_t ValueProfData::getSize(const InstrProfRecord &Record) { + uint32_t TotalSize = sizeof(ValueProfData); + uint32_t NumValueKinds = Record.getNumValueKinds(); + if (NumValueKinds == 0) + return TotalSize; + + for (uint32_t Kind = IPVK_First; Kind <= IPVK_Last; Kind++) { + uint32_t NumValueSites = Record.getNumValueSites(Kind); + if (!NumValueSites) + continue; + TotalSize += + ValueProfRecord::getSize(NumValueSites, Record.getNumValueData(Kind)); + } + return TotalSize; +} + +void ValueProfData::deserializeTo(InstrProfRecord &Record, + InstrProfRecord::ValueMapType *VMap) { + if (NumValueKinds == 0) + return; + + ValueProfRecord *VR = getFirstValueProfRecord(); + for (uint32_t K = 0; K < NumValueKinds; K++) { + VR->deserializeTo(Record, VMap); + VR = VR->getNext(); + } +} + +std::unique_ptr +ValueProfData::serializeFrom(const InstrProfRecord &Record) { + uint32_t TotalSize = getSize(Record); + std::unique_ptr VPD( + reinterpret_cast(new char[TotalSize])); + + VPD->TotalSize = TotalSize; + VPD->NumValueKinds = Record.getNumValueKinds(); + ValueProfRecord *VR = VPD->getFirstValueProfRecord(); + for (uint32_t Kind = IPVK_First; Kind <= IPVK_Last; Kind++) { + uint32_t NumValueSites = Record.getNumValueSites(Kind); + if (!NumValueSites) + continue; + VR->serializeFrom(Record, Kind, NumValueSites); + VR = VR->getNext(); + } + return VPD; +} + +ErrorOr> +ValueProfData::getValueProfData(const unsigned char *D, + const unsigned char *const BufferEnd, + support::endianness Endianness) { + using namespace support; + if (D + sizeof(ValueProfData) > BufferEnd) + return instrprof_error::truncated; + + uint32_t TotalSize = swapToHostOrder( + reinterpret_cast(D)[0], Endianness); + uint32_t NumValueKinds = swapToHostOrder( + reinterpret_cast(D)[1], Endianness); + + if (D + TotalSize > BufferEnd) + return instrprof_error::too_large; + if (NumValueKinds > IPVK_Last + 1) + return instrprof_error::malformed; + // Total size needs to be mulltiple of quadword size. + if (TotalSize % sizeof(uint64_t)) + return instrprof_error::malformed; + + std::unique_ptr VPD( + reinterpret_cast(new char[TotalSize])); + memcpy(VPD.get(), D, TotalSize); + // Byte swap. + VPD->swapBytesToHost(Endianness); + + // Data integrety check: + ValueProfRecord *VR = VPD->getFirstValueProfRecord(); + for (uint32_t K = 0; K < VPD->NumValueKinds; K++) { + if (VR->Kind > IPVK_Last) + return instrprof_error::malformed; + VR = VR->getNext(); + if ((char *)VR - (char *)VPD.get() > TotalSize) + return instrprof_error::malformed; + } + + D += TotalSize; + return std::move(VPD); +} + +void ValueProfData::swapBytesToHost(support::endianness Endianness) { + using namespace support; + if (Endianness == getHostEndianness()) + return; + + sys::swapByteOrder(TotalSize); + sys::swapByteOrder(NumValueKinds); + + ValueProfRecord *VR = getFirstValueProfRecord(); + for (uint32_t K = 0; K < NumValueKinds; K++) { + VR->swapBytes(Endianness, getHostEndianness()); + VR = VR->getNext(); + } +} + +void ValueProfData::swapBytesFromHost(support::endianness Endianness) { + using namespace support; + if (Endianness == getHostEndianness()) + return; + + ValueProfRecord *VR = getFirstValueProfRecord(); + for (uint32_t K = 0; K < NumValueKinds; K++) { + ValueProfRecord *NVR = VR->getNext(); + VR->swapBytes(getHostEndianness(), Endianness); + VR = NVR; + } + sys::swapByteOrder(TotalSize); + sys::swapByteOrder(NumValueKinds); +} + +ValueProfRecord *ValueProfData::getFirstValueProfRecord() { + return reinterpret_cast((char *)this + + sizeof(ValueProfData)); +} + +uint32_t ValueProfRecord::getNumValueData() const { + uint32_t NumValueData = 0; + for (uint32_t I = 0; I < NumValueSites; I++) + NumValueData += SiteCountArray[I]; + return NumValueData; +} + +ValueProfRecord *ValueProfRecord::getNext() { + return reinterpret_cast((char *)this + getSize()); +} + +InstrProfValueData *ValueProfRecord::getValueData() { + return reinterpret_cast((char *)this + + getHeaderSize(NumValueSites)); +} + +} // End of IndexedInstrProf namespace. } diff --git a/lib/ProfileData/InstrProfReader.cpp b/lib/ProfileData/InstrProfReader.cpp index 66fd961c84f..b968e6beafc 100644 --- a/lib/ProfileData/InstrProfReader.cpp +++ b/lib/ProfileData/InstrProfReader.cpp @@ -298,51 +298,16 @@ typedef InstrProfLookupTrait::offset_type offset_type; bool InstrProfLookupTrait::ReadValueProfilingData( const unsigned char *&D, const unsigned char *const End) { + ErrorOr> VDataPtrOrErr = + IndexedInstrProf::ValueProfData::getValueProfData( + D, End, ValueProfDataEndianness); - using namespace support; - // Read number of value kinds with value sites. - if (D + sizeof(uint64_t) > End) + if (VDataPtrOrErr.getError()) return false; - uint64_t ValueKindCount = endian::readNext(D); - - InstrProfRecord &ProfRecord = DataBuffer.back(); - for (uint32_t Kind = 0; Kind < ValueKindCount; ++Kind) { - - // Read value kind and number of value sites for kind. - if (D + 2 * sizeof(uint64_t) > End) - return false; - - uint64_t ValueKind = endian::readNext(D); - uint64_t ValueSiteCount = endian::readNext(D); - ProfRecord.reserveSites(ValueKind, ValueSiteCount); + VDataPtrOrErr.get()->deserializeTo(DataBuffer.back(), &HashKeys); + D += VDataPtrOrErr.get()->TotalSize; - for (uint64_t VSite = 0; VSite < ValueSiteCount; ++VSite) { - // Read number of value data pairs at value site. - if (D + sizeof(uint64_t) > End) - return false; - - uint64_t ValueDataCount = - endian::readNext(D); - - // Check if there are as many ValueDataPairs as ValueDataCount in memory. - if (D + (ValueDataCount << 1) * sizeof(uint64_t) > End) - return false; - - std::unique_ptr VDataPtr( - ValueDataCount == 0 ? nullptr - : new InstrProfValueData[ValueDataCount]); - - for (uint64_t VCount = 0; VCount < ValueDataCount; ++VCount) { - VDataPtr[VCount].Value = - endian::readNext(D); - VDataPtr[VCount].Count = - endian::readNext(D); - } - ProfRecord.addValueData(ValueKind, VSite, VDataPtr.get(), ValueDataCount, - &HashKeys); - } - } return true; } diff --git a/lib/ProfileData/InstrProfWriter.cpp b/lib/ProfileData/InstrProfWriter.cpp index b073d8df918..b6725df5278 100644 --- a/lib/ProfileData/InstrProfWriter.cpp +++ b/lib/ProfileData/InstrProfWriter.cpp @@ -20,6 +20,8 @@ using namespace llvm; namespace { +static support::endianness ValueProfDataEndianness = support::little; + class InstrProfRecordTrait { public: typedef StringRef key_type; @@ -51,20 +53,7 @@ public: M += ProfRecord.Counts.size() * sizeof(uint64_t); // Value data - M += sizeof(uint64_t); // Number of value kinds with value sites. - for (uint32_t Kind = IPVK_First; Kind <= IPVK_Last; ++Kind) { - uint32_t NumValueSites = ProfRecord.getNumValueSites(Kind); - if (NumValueSites == 0) - continue; - M += sizeof(uint64_t); // Value kind - M += sizeof(uint64_t); // The number of value sites for given value kind - for (uint32_t I = 0; I < NumValueSites; I++) { - M += sizeof(uint64_t); // Number of value data pairs at a value site - uint64_t NumValueDataForSite = - ProfRecord.getNumValueDataForSite(Kind, I); - M += 2 * sizeof(uint64_t) * NumValueDataForSite; // Value data pairs - } - } + M += IndexedInstrProf::ValueProfData::getSize(ProfileData.second); } LE.write(M); @@ -87,36 +76,12 @@ public: for (uint64_t I : ProfRecord.Counts) LE.write(I); - // Compute the number of value kinds with value sites. - uint64_t NumValueKinds = ProfRecord.getNumValueKinds(); - LE.write(NumValueKinds); - // Write value data - for (uint32_t Kind = IPVK_First; Kind <= IPVK_Last; ++Kind) { - uint32_t NumValueSites = ProfRecord.getNumValueSites(Kind); - if (NumValueSites == 0) - continue; - LE.write(Kind); // Write value kind - // Write number of value sites for current value kind - LE.write(NumValueSites); - - for (uint32_t I = 0; I < NumValueSites; I++) { - // Write number of value data pairs at this value site - uint64_t NumValueDataForSite = - ProfRecord.getNumValueDataForSite(Kind, I); - LE.write(NumValueDataForSite); - std::unique_ptr VD = - ProfRecord.getValueForSite(Kind, I); - - for (uint32_t V = 0; V < NumValueDataForSite; V++) { - if (Kind == IPVK_IndirectCallTarget) - LE.write(ComputeHash((const char *)VD[V].Value)); - else - LE.write(VD[V].Value); - LE.write(VD[V].Count); - } - } - } + std::unique_ptr VDataPtr = + IndexedInstrProf::ValueProfData::serializeFrom(ProfileData.second); + uint32_t S = VDataPtr->getSize(); + VDataPtr->swapBytesFromHost(ValueProfDataEndianness); + Out.write((const char *)VDataPtr.get(), S); } } }; @@ -148,6 +113,12 @@ static std::error_code combineInstrProfRecords(InstrProfRecord &Dest, return instrprof_error::success; } +// Internal interface for testing purpose only. +void InstrProfWriter::setValueProfDataEndianness( + support::endianness Endianness) { + ValueProfDataEndianness = Endianness; +} + void InstrProfWriter::updateStringTableReferences(InstrProfRecord &I) { I.updateStrings(&StringTable); } diff --git a/unittests/ProfileData/InstrProfTest.cpp b/unittests/ProfileData/InstrProfTest.cpp index 64c50bf3225..a9257fe316e 100644 --- a/unittests/ProfileData/InstrProfTest.cpp +++ b/unittests/ProfileData/InstrProfTest.cpp @@ -133,7 +133,54 @@ TEST_F(InstrProfTest, get_icall_data_read_write) { {(uint64_t) "callee2", 2}, {(uint64_t) "callee3", 3}}; Record1.addValueData(IPVK_IndirectCallTarget, 0, VD0, 3, nullptr); - // No valeu profile data at the second site. + // No value profile data at the second site. + Record1.addValueData(IPVK_IndirectCallTarget, 1, nullptr, 0, nullptr); + InstrProfValueData VD2[] = {{(uint64_t) "callee1", 1}, + {(uint64_t) "callee2", 2}}; + Record1.addValueData(IPVK_IndirectCallTarget, 2, VD2, 2, nullptr); + InstrProfValueData VD3[] = {{(uint64_t) "callee1", 1}}; + Record1.addValueData(IPVK_IndirectCallTarget, 3, VD3, 1, nullptr); + + Writer.addRecord(std::move(Record1)); + Writer.addRecord(std::move(Record2)); + Writer.addRecord(std::move(Record3)); + Writer.addRecord(std::move(Record4)); + auto Profile = Writer.writeBuffer(); + readProfile(std::move(Profile)); + + ErrorOr R = Reader->getInstrProfRecord("caller", 0x1234); + ASSERT_TRUE(NoError(R.getError())); + ASSERT_EQ(4U, R.get().getNumValueSites(IPVK_IndirectCallTarget)); + ASSERT_EQ(3U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 0)); + ASSERT_EQ(0U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 1)); + ASSERT_EQ(2U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 2)); + ASSERT_EQ(1U, R.get().getNumValueDataForSite(IPVK_IndirectCallTarget, 3)); + + std::unique_ptr VD = + R.get().getValueForSite(IPVK_IndirectCallTarget, 0); + // Now sort the target acording to frequency. + std::sort(&VD[0], &VD[3], + [](const InstrProfValueData &VD1, const InstrProfValueData &VD2) { + return VD1.Count > VD2.Count; + }); + ASSERT_EQ(StringRef((const char *)VD[0].Value, 7), StringRef("callee3")); + ASSERT_EQ(StringRef((const char *)VD[1].Value, 7), StringRef("callee2")); + ASSERT_EQ(StringRef((const char *)VD[2].Value, 7), StringRef("callee1")); +} + +TEST_F(InstrProfTest, get_icall_data_read_write_big_endian) { + InstrProfRecord Record1("caller", 0x1234, {1, 2}); + InstrProfRecord Record2("callee1", 0x1235, {3, 4}); + InstrProfRecord Record3("callee2", 0x1235, {3, 4}); + InstrProfRecord Record4("callee3", 0x1235, {3, 4}); + + // 4 value sites. + Record1.reserveSites(IPVK_IndirectCallTarget, 4); + InstrProfValueData VD0[] = {{(uint64_t) "callee1", 1}, + {(uint64_t) "callee2", 2}, + {(uint64_t) "callee3", 3}}; + Record1.addValueData(IPVK_IndirectCallTarget, 0, VD0, 3, nullptr); + // No value profile data at the second site. Record1.addValueData(IPVK_IndirectCallTarget, 1, nullptr, 0, nullptr); InstrProfValueData VD2[] = {{(uint64_t) "callee1", 1}, {(uint64_t) "callee2", 2}}; @@ -145,9 +192,16 @@ TEST_F(InstrProfTest, get_icall_data_read_write) { Writer.addRecord(std::move(Record2)); Writer.addRecord(std::move(Record3)); Writer.addRecord(std::move(Record4)); + + // Set big endian output. + Writer.setValueProfDataEndianness(support::big); + auto Profile = Writer.writeBuffer(); readProfile(std::move(Profile)); + // Set big endian input. + Reader->setValueProfDataEndianness(support::big); + ErrorOr R = Reader->getInstrProfRecord("caller", 0x1234); ASSERT_TRUE(NoError(R.getError())); ASSERT_EQ(4U, R.get().getNumValueSites(IPVK_IndirectCallTarget)); @@ -166,6 +220,9 @@ TEST_F(InstrProfTest, get_icall_data_read_write) { ASSERT_EQ(StringRef((const char *)VD[0].Value, 7), StringRef("callee3")); ASSERT_EQ(StringRef((const char *)VD[1].Value, 7), StringRef("callee2")); ASSERT_EQ(StringRef((const char *)VD[2].Value, 7), StringRef("callee1")); + + // Restore little endian default: + Writer.setValueProfDataEndianness(support::little); } TEST_F(InstrProfTest, get_icall_data_merge1) {