diff --git a/llvm/include/llvm/ProfileData/InstrProf.h b/llvm/include/llvm/ProfileData/InstrProf.h --- a/llvm/include/llvm/ProfileData/InstrProf.h +++ b/llvm/include/llvm/ProfileData/InstrProf.h @@ -684,6 +684,11 @@ /// Compute the overlap b/w this record and Input record. void overlap(InstrProfValueSiteRecord &Input, uint32_t ValueKind, OverlapStats &Overlap, OverlapStats &FuncLevelOverlap); + + void intersect(InstrProfValueSiteRecord &Input, + function_ref Warn); + void unique(InstrProfValueSiteRecord &Input, + function_ref Warn); }; /// Profiling information for a single function. @@ -784,6 +789,10 @@ OverlapStats &Overlap, OverlapStats &FuncLevelOverlap); + void intersect(InstrProfRecord &Other, + function_ref Warn); + void unique(InstrProfRecord &Other, function_ref Warn); + private: struct ValueProfData { std::vector IndirectCallSites; @@ -842,6 +851,11 @@ // Scale up value profile data count. void scaleValueProfData(uint32_t ValueKind, uint64_t Weight, function_ref Warn); + + void intersectValueProfData(uint32_t ValkeKind, InstrProfRecord &Src, + function_ref Warn); + void uniqueValueProfData(uint32_t ValkeKind, InstrProfRecord &Src, + function_ref Warn); }; struct NamedInstrProfRecord : InstrProfRecord { diff --git a/llvm/lib/ProfileData/InstrProf.cpp b/llvm/lib/ProfileData/InstrProf.cpp --- a/llvm/lib/ProfileData/InstrProf.cpp +++ b/llvm/lib/ProfileData/InstrProf.cpp @@ -635,6 +635,45 @@ } } +void InstrProfValueSiteRecord::intersect( + InstrProfValueSiteRecord &Input, function_ref Warn) { + this->sortByTargetValues(); + Input.sortByTargetValues(); + auto I = ValueData.begin(); + auto IE = ValueData.end(); + for (auto J = Input.ValueData.begin(), JE = Input.ValueData.end(); J != JE; + ++J) { + while (I != IE && I->Value < J->Value) + ++I; + if (I != IE && I->Value == J->Value) { + I->Count = std::min(I->Count, J->Count); + ++I; + continue; + } + ValueData.insert(I, *J); + } +} + +void InstrProfValueSiteRecord::unique( + InstrProfValueSiteRecord &Input, function_ref Warn) { + this->sortByTargetValues(); + Input.sortByTargetValues(); + auto I = ValueData.begin(); + auto IE = ValueData.end(); + for (auto J = Input.ValueData.begin(), JE = Input.ValueData.end(); J != JE; + ++J) { + while (I != IE && I->Value < J->Value) + ++I; + if (I != IE && I->Value == J->Value) { + if (J->Count > 0) + I->Count = 0; + ++I; + continue; + } + ValueData.insert(I, *J); + } +} + // Merge Value Profile data from Src record to this record for ValueKind. // Scale merged value counts by \p Weight. void InstrProfRecord::mergeValueProfData( @@ -696,6 +735,79 @@ scaleValueProfData(Kind, Weight, Warn); } +void InstrProfRecord::intersectValueProfData( + uint32_t ValueKind, InstrProfRecord &Src, + function_ref Warn) { + uint32_t ThisNumValueSites = getNumValueSites(ValueKind); + uint32_t OtherNumValueSites = Src.getNumValueSites(ValueKind); + if (ThisNumValueSites != OtherNumValueSites) { + Warn(instrprof_error::value_site_count_mismatch); + return; + } + if (!ThisNumValueSites) + return; + std::vector &ThisSiteRecords = + getOrCreateValueSitesForKind(ValueKind); + MutableArrayRef OtherSiteRecords = + Src.getValueSitesForKind(ValueKind); + for (uint32_t I = 0; I < ThisNumValueSites; I++) + ThisSiteRecords[I].intersect(OtherSiteRecords[I], Warn); +} + +void InstrProfRecord::intersect(InstrProfRecord &Other, + function_ref Warn) { + // If the number of counters doesn't match we either have bad data + // or a hash collision. + if (Counts.size() != Other.Counts.size()) { + Warn(instrprof_error::count_mismatch); + return; + } + + for (size_t I = 0, E = Other.Counts.size(); I < E; ++I) { + Counts[I] = std::min(Counts[I], Other.Counts[I]); + } + + for (uint32_t Kind = IPVK_First; Kind <= IPVK_Last; ++Kind) + intersectValueProfData(Kind, Other, Warn); +} + +void InstrProfRecord::uniqueValueProfData( + uint32_t ValueKind, InstrProfRecord &Src, + function_ref Warn) { + uint32_t ThisNumValueSites = getNumValueSites(ValueKind); + uint32_t OtherNumValueSites = Src.getNumValueSites(ValueKind); + if (ThisNumValueSites != OtherNumValueSites) { + Warn(instrprof_error::value_site_count_mismatch); + return; + } + if (!ThisNumValueSites) + return; + std::vector &ThisSiteRecords = + getOrCreateValueSitesForKind(ValueKind); + MutableArrayRef OtherSiteRecords = + Src.getValueSitesForKind(ValueKind); + for (uint32_t I = 0; I < ThisNumValueSites; I++) + ThisSiteRecords[I].intersect(OtherSiteRecords[I], Warn); +} + +void InstrProfRecord::unique(InstrProfRecord &Other, + function_ref Warn) { + // If the number of counters doesn't match we either have bad data + // or a hash collision. + if (Counts.size() != Other.Counts.size()) { + Warn(instrprof_error::count_mismatch); + return; + } + + for (size_t I = 0, E = Other.Counts.size(); I < E; ++I) { + if (Other.Counts[I] > 0) + Counts[I] = 0; + } + + for (uint32_t Kind = IPVK_First; Kind <= IPVK_Last; ++Kind) + uniqueValueProfData(Kind, Other, Warn); +} + // Map indirect call target name hash to name string. uint64_t InstrProfRecord::remapValue(uint64_t Value, uint32_t ValueKind, InstrProfSymtab *SymTab) { diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -778,6 +778,152 @@ return 0; } +static void setIntersectInstrProfile(const std::string &BaseFilename, + const std::string &TestFilename, + const std::string &OutputFilename) { + if (OutputFilename.compare("-") == 0) + exitWithError("Cannot write indexed profdata format to stdout."); + + std::mutex ErrorLock; + SmallSet WriterErrorCodes; + WriterContext WC(false, ErrorLock, WriterErrorCodes); + WeightedFile BaseInput{BaseFilename, 1}; + WeightedFile TestInput{TestFilename, 1}; + + auto BaseReaderOrErr = InstrProfReader::create(BaseInput.Filename); + if (Error E = BaseReaderOrErr.takeError()) { + exitWithError("InstrProfReader Error 1"); + } + auto TestReaderOrErr = IndexedInstrProfReader::create(TestInput.Filename); + if (Error E = TestReaderOrErr.takeError()) { + exitWithError("InstrProfReader Error 1"); + } + + auto BaseReader = std::move(BaseReaderOrErr.get()); + auto TestReader = std::move(TestReaderOrErr.get()); + + bool IsIRProfile = BaseReader->isIRLevelProfile(); + bool HasCSIRProfile = BaseReader->hasCSIRLevelProfile(); + if (WC.Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) { + exitWithError("InstrProfReader Error 2"); + } + WC.Writer.setInstrEntryBBEnabled(BaseReader->instrEntryBBEnabled()); + + for (auto &I : *BaseReader) { + const StringRef FuncName = I.Name; + const auto FuncHash = I.Hash; + + auto RecordOrErr = TestReader->getInstrProfRecord(FuncName, FuncHash); + if (Error E = RecordOrErr.takeError()) { + // swallow error + InstrProfError::take(std::move(E)); + continue; + } + + auto Record = std::move(RecordOrErr.get()); + I.intersect(Record, [&](instrprof_error E) { + // TODO + }); + + WC.Writer.addRecord(std::move(I), BaseInput.Weight, [&](Error E) {}); + } + + writeInstrProfile(OutputFilename, PF_Binary, WC.Writer); +} + +static int intersect_main(int argc, const char *argv[]) { + cl::opt BaseFilename(cl::Positional, cl::Required, + cl::desc("")); + cl::opt TestFilename(cl::Positional, cl::Required, + cl::desc("")); + cl::opt OutputFilename("output", cl::value_desc("output"), + cl::init("-"), cl::Required, + cl::desc("Output file")); + cl::alias OutputA("o", cl::desc("Alias for --output"), + cl::aliasopt(OutputFilename)); + cl::ParseCommandLineOptions( + argc, argv, + "LLVM profile data tool that performs the following set operation: INTERSECT \n"); + + setIntersectInstrProfile(BaseFilename, TestFilename, OutputFilename); + + return 0; +} + +static void setUniqueInstrProfile(const std::string &BaseFilename, + const std::string &TestFilename, + const std::string &OutputFilename) { + if (OutputFilename.compare("-") == 0) + exitWithError("Cannot write indexed profdata format to stdout."); + + std::mutex ErrorLock; + SmallSet WriterErrorCodes; + WriterContext WC(false, ErrorLock, WriterErrorCodes); + WeightedFile BaseInput{BaseFilename, 1}; + WeightedFile TestInput{TestFilename, 1}; + + auto BaseReaderOrErr = InstrProfReader::create(BaseInput.Filename); + if (Error E = BaseReaderOrErr.takeError()) { + exitWithError("InstrProfReader Error 1"); + } + auto TestReaderOrErr = IndexedInstrProfReader::create(TestInput.Filename); + if (Error E = TestReaderOrErr.takeError()) { + exitWithError("InstrProfReader Error 1"); + } + + auto BaseReader = std::move(BaseReaderOrErr.get()); + auto TestReader = std::move(TestReaderOrErr.get()); + + bool IsIRProfile = BaseReader->isIRLevelProfile(); + bool HasCSIRProfile = BaseReader->hasCSIRLevelProfile(); + if (WC.Writer.setIsIRLevelProfile(IsIRProfile, HasCSIRProfile)) { + exitWithError("InstrProfReader Error 2"); + } + WC.Writer.setInstrEntryBBEnabled(BaseReader->instrEntryBBEnabled()); + + for (auto &I : *BaseReader) { + const StringRef FuncName = I.Name; + const auto FuncHash = I.Hash; + + auto RecordOrErr = TestReader->getInstrProfRecord(FuncName, FuncHash); + + if (Error E = RecordOrErr.takeError()) { + // swallow error + InstrProfError::take(std::move(E)); + } else { + auto Record = std::move(RecordOrErr.get()); + I.unique(Record, [&](instrprof_error E) { + // TODO + }); + } + + WC.Writer.addRecord(std::move(I), BaseInput.Weight, [&](Error E) {}); + } + + writeInstrProfile(OutputFilename, PF_Binary, WC.Writer); +} + +static int unique_main(int argc, const char *argv[]) { + cl::opt BaseFilename(cl::Positional, cl::Required, + cl::desc("")); + cl::opt TestFilename(cl::Positional, cl::Required, + cl::desc("")); + cl::opt OutputFilename("output", cl::value_desc("output"), + cl::init("-"), cl::Required, + cl::desc("Output file")); + cl::alias OutputA("o", cl::desc("Alias for --output"), + cl::aliasopt(OutputFilename)); + cl::ParseCommandLineOptions( + argc, argv, + "LLVM profile data tool that performs the following set operation: NOT IN \n"); + + setUniqueInstrProfile(BaseFilename, TestFilename, OutputFilename); + + return 0; +} + typedef struct ValueSitesStats { ValueSitesStats() : TotalNumValueSites(0), TotalNumValueSitesWithValueProfile(0), @@ -1319,6 +1465,11 @@ func = show_main; else if (strcmp(argv[1], "overlap") == 0) func = overlap_main; + // Other useful operators for differential coverage analysis + else if (strcmp(argv[1], "intersect") == 0) + func = intersect_main; + else if (strcmp(argv[1], "unique") == 0) + func = unique_main; if (func) { std::string Invocation(ProgName.str() + " " + argv[1]);