Index: llvm/trunk/docs/CommandGuide/llvm-profdata.rst =================================================================== --- llvm/trunk/docs/CommandGuide/llvm-profdata.rst +++ llvm/trunk/docs/CommandGuide/llvm-profdata.rst @@ -28,7 +28,7 @@ SYNOPSIS ^^^^^^^^ -:program:`llvm-profdata merge` [*options*] [*filenames...*] +:program:`llvm-profdata merge` [*options*] [*filename...*] DESCRIPTION ^^^^^^^^^^^ @@ -37,6 +37,14 @@ generated by PGO instrumentation and merges them together into a single indexed profile data file. +By default profile data is merged without modification. This means that the +relative importance of each input file is proportional to the number of samples +or counts it contains. In general, the input from a longer training run will be +interpreted as relatively more important than a shorter run. Depending on the +nature of the training runs it may be useful to adjust the weight given to each +input file by using the ``-weighted-input`` option. + + OPTIONS ^^^^^^^ @@ -49,6 +57,13 @@ Specify the output file name. *Output* cannot be ``-`` as the resulting indexed profile data can't be written to standard output. +.. option:: -weighted-input=weight,filename + + Specify an input file name along with a weight. The profile counts of the input + file will be scaled (multiplied) by the supplied ``weight``, where where ``weight`` + is a decimal integer >= 1. Input files specified without using this option are + assigned a default weight of 1. Examples are shown below. + .. option:: -instr (default) Specify that the input profile is an instrumentation-based profile. @@ -75,6 +90,30 @@ Emit the profile using GCC's gcov format (Not yet supported). +EXAMPLES +^^^^^^^^ +Basic Usage ++++++++++++ +Merge three profiles: + +:: + + llvm-profdata merge foo.profdata bar.profdata baz.profdata -output merged.profdata + +Weighted Input +++++++++++++++ +The input file `foo.profdata` is especially important, multiply its counts by 10: + +:: + + llvm-profdata merge -weighted-input=10,foo.profdata bar.profdata baz.profdata -output merged.profdata + +Exactly equivalent to the previous invocation (explicit form; useful for programmatic invocation): + +:: + + llvm-profdata merge -weighted-input=10,foo.profdata -weighted-input=1,bar.profdata -weighted-input=1,baz.profdata -output merged.profdata + .. program:: llvm-profdata show .. _profdata-show: Index: llvm/trunk/include/llvm/ProfileData/InstrProf.h =================================================================== --- llvm/trunk/include/llvm/ProfileData/InstrProf.h +++ llvm/trunk/include/llvm/ProfileData/InstrProf.h @@ -219,7 +219,8 @@ } /// Merge data from another InstrProfValueSiteRecord - void mergeValueData(InstrProfValueSiteRecord &Input) { + /// Optionally scale merged counts by \p Weight. + void mergeValueData(InstrProfValueSiteRecord &Input, uint64_t Weight = 1) { this->sortByTargetValues(); Input.sortByTargetValues(); auto I = ValueData.begin(); @@ -229,7 +230,15 @@ while (I != IE && I->Value < J->Value) ++I; if (I != IE && I->Value == J->Value) { - I->Count = SaturatingAdd(I->Count, J->Count); + // FIXME: Improve handling of counter overflow. + uint64_t JCount = J->Count; + bool Overflowed; + if (Weight > 1) { + JCount = SaturatingMultiply(JCount, Weight, &Overflowed); + assert(!Overflowed && "Value data counter overflowed!"); + } + I->Count = SaturatingAdd(I->Count, JCount, &Overflowed); + assert(!Overflowed && "Value data counter overflowed!"); ++I; continue; } @@ -275,7 +284,8 @@ ValueMapType *HashKeys); /// Merge the counts in \p Other into this one. - inline instrprof_error merge(InstrProfRecord &Other); + /// Optionally scale merged counts by \p Weight. + inline instrprof_error merge(InstrProfRecord &Other, uint64_t Weight = 1); /// Used by InstrProfWriter: update the value strings to commoned strings in /// the writer instance. @@ -327,7 +337,9 @@ } // Merge Value Profile data from Src record to this record for ValueKind. - instrprof_error mergeValueProfData(uint32_t ValueKind, InstrProfRecord &Src) { + // Scale merged value counts by \p Weight. + instrprof_error mergeValueProfData(uint32_t ValueKind, InstrProfRecord &Src, + uint64_t Weight) { uint32_t ThisNumValueSites = getNumValueSites(ValueKind); uint32_t OtherNumValueSites = Src.getNumValueSites(ValueKind); if (ThisNumValueSites != OtherNumValueSites) @@ -337,7 +349,7 @@ std::vector &OtherSiteRecords = Src.getValueSitesForKind(ValueKind); for (uint32_t I = 0; I < ThisNumValueSites; I++) - ThisSiteRecords[I].mergeValueData(OtherSiteRecords[I]); + ThisSiteRecords[I].mergeValueData(OtherSiteRecords[I], Weight); return instrprof_error::success; } }; @@ -423,7 +435,8 @@ VData.Value = (uint64_t)StrTab->insertString((const char *)VData.Value); } -instrprof_error InstrProfRecord::merge(InstrProfRecord &Other) { +instrprof_error InstrProfRecord::merge(InstrProfRecord &Other, + uint64_t Weight) { // If the number of counters doesn't match we either have bad data // or a hash collision. if (Counts.size() != Other.Counts.size()) @@ -432,14 +445,20 @@ instrprof_error Result = instrprof_error::success; for (size_t I = 0, E = Other.Counts.size(); I < E; ++I) { - bool ResultOverflowed; - Counts[I] = SaturatingAdd(Counts[I], Other.Counts[I], &ResultOverflowed); - if (ResultOverflowed) + bool Overflowed; + uint64_t OtherCount = Other.Counts[I]; + if (Weight > 1) { + OtherCount = SaturatingMultiply(OtherCount, Weight, &Overflowed); + if (Overflowed) + Result = instrprof_error::counter_overflow; + } + Counts[I] = SaturatingAdd(Counts[I], OtherCount, &Overflowed); + if (Overflowed) Result = instrprof_error::counter_overflow; } for (uint32_t Kind = IPVK_First; Kind <= IPVK_Last; ++Kind) { - instrprof_error MergeValueResult = mergeValueProfData(Kind, Other); + instrprof_error MergeValueResult = mergeValueProfData(Kind, Other, Weight); if (MergeValueResult != instrprof_error::success) Result = MergeValueResult; } Index: llvm/trunk/include/llvm/ProfileData/InstrProfWriter.h =================================================================== --- llvm/trunk/include/llvm/ProfileData/InstrProfWriter.h +++ llvm/trunk/include/llvm/ProfileData/InstrProfWriter.h @@ -39,8 +39,8 @@ void updateStringTableReferences(InstrProfRecord &I); /// Add function counts for the given function. If there are already counts /// for this function and the hash and number of counts match, each counter is - /// summed. - std::error_code addRecord(InstrProfRecord &&I); + /// summed. Optionally scale counts by \p Weight. + std::error_code addRecord(InstrProfRecord &&I, uint64_t Weight = 1); /// Write the profile to \c OS void write(raw_fd_ostream &OS); /// Write the profile in text format to \c OS Index: llvm/trunk/include/llvm/ProfileData/SampleProf.h =================================================================== --- llvm/trunk/include/llvm/ProfileData/SampleProf.h +++ llvm/trunk/include/llvm/ProfileData/SampleProf.h @@ -123,18 +123,36 @@ SampleRecord() : NumSamples(0), CallTargets() {} /// Increment the number of samples for this record by \p S. + /// Optionally scale sample count \p S by \p Weight. /// /// Sample counts accumulate using saturating arithmetic, to avoid wrapping /// around unsigned integers. - void addSamples(uint64_t S) { NumSamples = SaturatingAdd(NumSamples, S); } + void addSamples(uint64_t S, uint64_t Weight = 1) { + // FIXME: Improve handling of counter overflow. + bool Overflowed; + if (Weight > 1) { + S = SaturatingMultiply(S, Weight, &Overflowed); + assert(!Overflowed && "Sample counter overflowed!"); + } + NumSamples = SaturatingAdd(NumSamples, S, &Overflowed); + assert(!Overflowed && "Sample counter overflowed!"); + } /// Add called function \p F with samples \p S. + /// Optionally scale sample count \p S by \p Weight. /// /// Sample counts accumulate using saturating arithmetic, to avoid wrapping /// around unsigned integers. - void addCalledTarget(StringRef F, uint64_t S) { + void addCalledTarget(StringRef F, uint64_t S, uint64_t Weight = 1) { + // FIXME: Improve handling of counter overflow. uint64_t &TargetSamples = CallTargets[F]; - TargetSamples = SaturatingAdd(TargetSamples, S); + bool Overflowed; + if (Weight > 1) { + S = SaturatingMultiply(S, Weight, &Overflowed); + assert(!Overflowed && "Called target counter overflowed!"); + } + TargetSamples = SaturatingAdd(TargetSamples, S, &Overflowed); + assert(!Overflowed && "Called target counter overflowed!"); } /// Return true if this sample record contains function calls. @@ -144,10 +162,11 @@ const CallTargetMap &getCallTargets() const { return CallTargets; } /// Merge the samples in \p Other into this record. - void merge(const SampleRecord &Other) { - addSamples(Other.getSamples()); + /// Optionally scale sample counts by \p Weight. + void merge(const SampleRecord &Other, uint64_t Weight = 1) { + addSamples(Other.getSamples(), Weight); for (const auto &I : Other.getCallTargets()) - addCalledTarget(I.first(), I.second); + addCalledTarget(I.first(), I.second, Weight); } void print(raw_ostream &OS, unsigned Indent) const; @@ -174,16 +193,36 @@ FunctionSamples() : TotalSamples(0), TotalHeadSamples(0) {} void print(raw_ostream &OS = dbgs(), unsigned Indent = 0) const; void dump() const; - void addTotalSamples(uint64_t Num) { TotalSamples += Num; } - void addHeadSamples(uint64_t Num) { TotalHeadSamples += Num; } - void addBodySamples(uint32_t LineOffset, uint32_t Discriminator, - uint64_t Num) { - BodySamples[LineLocation(LineOffset, Discriminator)].addSamples(Num); + void addTotalSamples(uint64_t Num, uint64_t Weight = 1) { + // FIXME: Improve handling of counter overflow. + bool Overflowed; + if (Weight > 1) { + Num = SaturatingMultiply(Num, Weight, &Overflowed); + assert(!Overflowed && "Total samples counter overflowed!"); + } + TotalSamples = SaturatingAdd(TotalSamples, Num, &Overflowed); + assert(!Overflowed && "Total samples counter overflowed!"); + } + void addHeadSamples(uint64_t Num, uint64_t Weight = 1) { + // FIXME: Improve handling of counter overflow. + bool Overflowed; + if (Weight > 1) { + Num = SaturatingMultiply(Num, Weight, &Overflowed); + assert(!Overflowed && "Total head samples counter overflowed!"); + } + TotalHeadSamples = SaturatingAdd(TotalHeadSamples, Num, &Overflowed); + assert(!Overflowed && "Total head samples counter overflowed!"); + } + void addBodySamples(uint32_t LineOffset, uint32_t Discriminator, uint64_t Num, + uint64_t Weight = 1) { + BodySamples[LineLocation(LineOffset, Discriminator)].addSamples(Num, + Weight); } void addCalledTargetSamples(uint32_t LineOffset, uint32_t Discriminator, - std::string FName, uint64_t Num) { - BodySamples[LineLocation(LineOffset, Discriminator)].addCalledTarget(FName, - Num); + std::string FName, uint64_t Num, + uint64_t Weight = 1) { + BodySamples[LineLocation(LineOffset, Discriminator)].addCalledTarget( + FName, Num, Weight); } /// Return the number of samples collected at the given location. @@ -232,18 +271,19 @@ } /// Merge the samples in \p Other into this one. - void merge(const FunctionSamples &Other) { - addTotalSamples(Other.getTotalSamples()); - addHeadSamples(Other.getHeadSamples()); + /// Optionally scale samples by \p Weight. + void merge(const FunctionSamples &Other, uint64_t Weight = 1) { + addTotalSamples(Other.getTotalSamples(), Weight); + addHeadSamples(Other.getHeadSamples(), Weight); for (const auto &I : Other.getBodySamples()) { const LineLocation &Loc = I.first; const SampleRecord &Rec = I.second; - BodySamples[Loc].merge(Rec); + BodySamples[Loc].merge(Rec, Weight); } for (const auto &I : Other.getCallsiteSamples()) { const CallsiteLocation &Loc = I.first; const FunctionSamples &Rec = I.second; - functionSamplesAt(Loc).merge(Rec); + functionSamplesAt(Loc).merge(Rec, Weight); } } Index: llvm/trunk/lib/ProfileData/InstrProfWriter.cpp =================================================================== --- llvm/trunk/lib/ProfileData/InstrProfWriter.cpp +++ llvm/trunk/lib/ProfileData/InstrProfWriter.cpp @@ -98,7 +98,8 @@ I.updateStrings(&StringTable); } -std::error_code InstrProfWriter::addRecord(InstrProfRecord &&I) { +std::error_code InstrProfWriter::addRecord(InstrProfRecord &&I, + uint64_t Weight) { updateStringTableReferences(I); auto &ProfileDataMap = FunctionData[I.Name]; @@ -113,9 +114,18 @@ // We've never seen a function with this name and hash, add it. Dest = std::move(I); Result = instrprof_error::success; + if (Weight > 1) { + for (auto &Count : Dest.Counts) { + bool Overflowed; + Count = SaturatingMultiply(Count, Weight, &Overflowed); + if (Overflowed && Result == instrprof_error::success) { + Result = instrprof_error::counter_overflow; + } + } + } } else { // We're updating a function we've seen before. - Result = Dest.merge(I); + Result = Dest.merge(I, Weight); } // We keep track of the max function count as we go for simplicity. Index: llvm/trunk/test/tools/llvm-profdata/Inputs/weight-sample-bar.proftext =================================================================== --- llvm/trunk/test/tools/llvm-profdata/Inputs/weight-sample-bar.proftext +++ llvm/trunk/test/tools/llvm-profdata/Inputs/weight-sample-bar.proftext @@ -0,0 +1,8 @@ +bar:1772037:35370 + 17: 35370 + 18: 35370 + 19: 7005 + 20: 29407 + 21: 12170 + 23: 18150 bar:19829 + 25: 36666 Index: llvm/trunk/test/tools/llvm-profdata/Inputs/weight-sample-foo.proftext =================================================================== --- llvm/trunk/test/tools/llvm-profdata/Inputs/weight-sample-foo.proftext +++ llvm/trunk/test/tools/llvm-profdata/Inputs/weight-sample-foo.proftext @@ -0,0 +1,8 @@ +foo:1763288:35327 + 7: 35327 + 8: 35327 + 9: 6930 + 10: 29341 + 11: 11906 + 13: 18185 foo:19531 + 15: 36458 Index: llvm/trunk/test/tools/llvm-profdata/text-format-errors.test =================================================================== --- llvm/trunk/test/tools/llvm-profdata/text-format-errors.test +++ llvm/trunk/test/tools/llvm-profdata/text-format-errors.test @@ -2,7 +2,7 @@ 1- Detect invalid count RUN: not llvm-profdata show %p/Inputs/invalid-count-later.proftext 2>&1 | FileCheck %s --check-prefix=INVALID-COUNT-LATER -RUN: not llvm-profdata merge %p/Inputs/invalid-count-later.proftext %p/Inputs/invalid-count-later.profdata -o %t.out 2>&1 | FileCheck %s --check-prefix=INVALID-COUNT-LATER +RUN: not llvm-profdata merge %p/Inputs/invalid-count-later.proftext %p/Inputs/invalid-count-later.proftext -o %t.out 2>&1 | FileCheck %s --check-prefix=INVALID-COUNT-LATER INVALID-COUNT-LATER: error: {{.*}}invalid-count-later.proftext: Malformed instrumentation profile data 2- Detect bad hash Index: llvm/trunk/test/tools/llvm-profdata/weight-instr.test =================================================================== --- llvm/trunk/test/tools/llvm-profdata/weight-instr.test +++ llvm/trunk/test/tools/llvm-profdata/weight-instr.test @@ -0,0 +1,69 @@ +Tests for weighted merge of instrumented profiles. + +1- Merge the foo and bar profiles with unity weight and verify the combined output +RUN: llvm-profdata merge -instr -weighted-input=1,%p/Inputs/weight-instr-bar.profdata -weighted-input=1,%p/Inputs/weight-instr-foo.profdata -o %t +RUN: llvm-profdata show -instr -all-functions %t | FileCheck %s -check-prefix=1X_1X_WEIGHT +RUN: llvm-profdata merge -instr -weighted-input=1,%p/Inputs/weight-instr-bar.profdata %p/Inputs/weight-instr-foo.profdata -o %t +RUN: llvm-profdata show -instr -all-functions %t | FileCheck %s -check-prefix=1X_1X_WEIGHT +1X_1X_WEIGHT: Counters: +1X_1X_WEIGHT-NEXT: usage: +1X_1X_WEIGHT-NEXT: Hash: 0x0000000000000000 +1X_1X_WEIGHT-NEXT: Counters: 1 +1X_1X_WEIGHT-NEXT: Function count: 0 +1X_1X_WEIGHT-NEXT: foo: +1X_1X_WEIGHT-NEXT: Hash: 0x000000000000028a +1X_1X_WEIGHT-NEXT: Counters: 3 +1X_1X_WEIGHT-NEXT: Function count: 866988873 +1X_1X_WEIGHT-NEXT: bar: +1X_1X_WEIGHT-NEXT: Hash: 0x000000000000028a +1X_1X_WEIGHT-NEXT: Counters: 3 +1X_1X_WEIGHT-NEXT: Function count: 866988873 +1X_1X_WEIGHT-NEXT: main: +1X_1X_WEIGHT-NEXT: Hash: 0x7d31c47ea98f8248 +1X_1X_WEIGHT-NEXT: Counters: 60 +1X_1X_WEIGHT-NEXT: Function count: 2 +1X_1X_WEIGHT-NEXT: Functions shown: 4 +1X_1X_WEIGHT-NEXT: Total functions: 4 +1X_1X_WEIGHT-NEXT: Maximum function count: 866988873 +1X_1X_WEIGHT-NEXT: Maximum internal block count: 267914296 + +2- Merge the foo and bar profiles with weight 3x and 5x respectively and verify the combined output +RUN: llvm-profdata merge -instr -weighted-input=3,%p/Inputs/weight-instr-bar.profdata -weighted-input=5,%p/Inputs/weight-instr-foo.profdata -o %t +RUN: llvm-profdata show -instr -all-functions %t | FileCheck %s -check-prefix=3X_5X_WEIGHT +3X_5X_WEIGHT: Counters: +3X_5X_WEIGHT-NEXT: usage: +3X_5X_WEIGHT-NEXT: Hash: 0x0000000000000000 +3X_5X_WEIGHT-NEXT: Counters: 1 +3X_5X_WEIGHT-NEXT: Function count: 0 +3X_5X_WEIGHT-NEXT: foo: +3X_5X_WEIGHT-NEXT: Hash: 0x000000000000028a +3X_5X_WEIGHT-NEXT: Counters: 3 +3X_5X_WEIGHT-NEXT: Function count: 4334944365 +3X_5X_WEIGHT-NEXT: bar: +3X_5X_WEIGHT-NEXT: Hash: 0x000000000000028a +3X_5X_WEIGHT-NEXT: Counters: 3 +3X_5X_WEIGHT-NEXT: Function count: 2600966619 +3X_5X_WEIGHT-NEXT: main: +3X_5X_WEIGHT-NEXT: Hash: 0x7d31c47ea98f8248 +3X_5X_WEIGHT-NEXT: Counters: 60 +3X_5X_WEIGHT-NEXT: Function count: 8 +3X_5X_WEIGHT-NEXT: Functions shown: 4 +3X_5X_WEIGHT-NEXT: Total functions: 4 +3X_5X_WEIGHT-NEXT: Maximum function count: 4334944365 +3X_5X_WEIGHT-NEXT: Maximum internal block count: 1339571480 + +3- Bad merge: invalid weight +RUN: not llvm-profdata merge -instr -weighted-input=3,%p/Inputs/weight-instr-bar.profdata -weighted-input=0,%p/Inputs/weight-instr-foo.profdata -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT +RUN: not llvm-profdata merge -instr -weighted-input=3,%p/Inputs/weight-instr-bar.profdata -weighted-input=0.75,%p/Inputs/weight-instr-foo.profdata -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT +RUN: not llvm-profdata merge -instr -weighted-input=3,%p/Inputs/weight-instr-bar.profdata -weighted-input=-5,%p/Inputs/weight-instr-foo.profdata -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT +RUN: not llvm-profdata merge -instr -weighted-input=3,%p/Inputs/weight-instr-bar.profdata -weighted-input=,%p/Inputs/weight-instr-foo.profdata -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT +RUN: not llvm-profdata merge -instr -weighted-input=3,%p/Inputs/weight-instr-bar.profdata -weighted-input=%p/Inputs/weight-instr-foo.profdata -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT +INVALID_WEIGHT: error: Input weight must be a positive integer. + +4- Bad merge: input path does not exist +RUN: not llvm-profdata merge -instr -weighted-input=3,%p/Inputs/does-not-exist.profdata -weighted-input=2,%p/Inputs/does-not-exist-either.profdata -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_INPUT +INVALID_INPUT: {{.*}}: {{.*}}does-not-exist.profdata: {{[Nn]}}o such file or directory + +5- No inputs +RUN: not llvm-profdata merge -instr -o %t.out 2>&1 | FileCheck %s -check-prefix=NO_INPUT +NO_INPUT: {{.*}}: No input files specified. See llvm-profdata{{(\.EXE|\.exe)?}} merge -help Index: llvm/trunk/test/tools/llvm-profdata/weight-sample.test =================================================================== --- llvm/trunk/test/tools/llvm-profdata/weight-sample.test +++ llvm/trunk/test/tools/llvm-profdata/weight-sample.test @@ -0,0 +1,56 @@ +Tests for weighted merge of sample profiles. + +1- Merge the foo and bar profiles with unity weight and verify the combined output +RUN: llvm-profdata merge -sample -text -weighted-input=1,%p/Inputs/weight-sample-bar.proftext -weighted-input=1,%p/Inputs/weight-sample-foo.proftext -o - | FileCheck %s -check-prefix=1X_1X_WEIGHT +RUN: llvm-profdata merge -sample -text -weighted-input=1,%p/Inputs/weight-sample-bar.proftext %p/Inputs/weight-sample-foo.proftext -o - | FileCheck %s -check-prefix=1X_1X_WEIGHT +1X_1X_WEIGHT: foo:1763288:35327 +1X_1X_WEIGHT-NEXT: 7: 35327 +1X_1X_WEIGHT-NEXT: 8: 35327 +1X_1X_WEIGHT-NEXT: 9: 6930 +1X_1X_WEIGHT-NEXT: 10: 29341 +1X_1X_WEIGHT-NEXT: 11: 11906 +1X_1X_WEIGHT-NEXT: 13: 18185 foo:19531 +1X_1X_WEIGHT-NEXT: 15: 36458 +1X_1X_WEIGHT-NEXT: bar:1772037:35370 +1X_1X_WEIGHT-NEXT: 17: 35370 +1X_1X_WEIGHT-NEXT: 18: 35370 +1X_1X_WEIGHT-NEXT: 19: 7005 +1X_1X_WEIGHT-NEXT: 20: 29407 +1X_1X_WEIGHT-NEXT: 21: 12170 +1X_1X_WEIGHT-NEXT: 23: 18150 bar:19829 +1X_1X_WEIGHT-NEXT: 25: 36666 + +2- Merge the foo and bar profiles with weight 3x and 5x respectively and verify the combined output +RUN: llvm-profdata merge -sample -text -weighted-input=3,%p/Inputs/weight-sample-bar.proftext -weighted-input=5,%p/Inputs/weight-sample-foo.proftext -o - | FileCheck %s -check-prefix=3X_5X_WEIGHT +3X_5X_WEIGHT: foo:8816440:176635 +3X_5X_WEIGHT-NEXT: 7: 176635 +3X_5X_WEIGHT-NEXT: 8: 176635 +3X_5X_WEIGHT-NEXT: 9: 34650 +3X_5X_WEIGHT-NEXT: 10: 146705 +3X_5X_WEIGHT-NEXT: 11: 59530 +3X_5X_WEIGHT-NEXT: 13: 90925 foo:97655 +3X_5X_WEIGHT-NEXT: 15: 182290 +3X_5X_WEIGHT-NEXT: bar:5316111:106110 +3X_5X_WEIGHT-NEXT: 17: 106110 +3X_5X_WEIGHT-NEXT: 18: 106110 +3X_5X_WEIGHT-NEXT: 19: 21015 +3X_5X_WEIGHT-NEXT: 20: 88221 +3X_5X_WEIGHT-NEXT: 21: 36510 +3X_5X_WEIGHT-NEXT: 23: 54450 bar:59487 +3X_5X_WEIGHT-NEXT: 25: 109998 + +3- Bad merge: invalid weight +RUN: not llvm-profdata merge -sample -weighted-input=3,%p/Inputs/weight-sample-bar.proftext -weighted-input=0,%p/Inputs/weight-sample-foo.proftext -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT +RUN: not llvm-profdata merge -sample -weighted-input=3,%p/Inputs/weight-sample-bar.proftext -weighted-input=0.75,%p/Inputs/weight-sample-foo.proftext -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT +RUN: not llvm-profdata merge -sample -weighted-input=3,%p/Inputs/weight-sample-bar.proftext -weighted-input=-5,%p/Inputs/weight-sample-foo.proftext -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT +RUN: not llvm-profdata merge -sample -weighted-input=3,%p/Inputs/weight-sample-bar.proftext -weighted-input=,%p/Inputs/weight-sample-foo.proftext -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT +RUN: not llvm-profdata merge -sample -weighted-input=3,%p/Inputs/weight-sample-bar.proftext -weighted-input=%p/Inputs/weight-sample-foo.proftext -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_WEIGHT +INVALID_WEIGHT: error: Input weight must be a positive integer. + +4- Bad merge: input path does not exist +RUN: not llvm-profdata merge -sample -weighted-input=3,%p/Inputs/does-not-exist.proftext -weighted-input=2,%p/Inputs/does-not-exist-either.proftext -o %t.out 2>&1 | FileCheck %s -check-prefix=INVALID_INPUT +INVALID_INPUT: {{.*}}: {{.*}}does-not-exist.proftext: {{[Nn]}}o such file or directory + +5- No inputs +RUN: not llvm-profdata merge -sample -o %t.out 2>&1 | FileCheck %s -check-prefix=NO_INPUT +NO_INPUT: {{.*}}: No input files specified. See llvm-profdata{{(\.EXE|\.exe)?}} merge -help Index: llvm/trunk/tools/llvm-profdata/llvm-profdata.cpp =================================================================== --- llvm/trunk/tools/llvm-profdata/llvm-profdata.cpp +++ llvm/trunk/tools/llvm-profdata/llvm-profdata.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/IR/LLVMContext.h" #include "llvm/ProfileData/InstrProfReader.h" @@ -19,6 +20,7 @@ #include "llvm/ProfileData/SampleProfReader.h" #include "llvm/ProfileData/SampleProfWriter.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" #include "llvm/Support/ManagedStatic.h" @@ -27,6 +29,8 @@ #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" +#include +#include using namespace llvm; @@ -91,7 +95,17 @@ } } -static void mergeInstrProfile(const cl::list &Inputs, +struct WeightedFile { + StringRef Filename; + uint64_t Weight; + + WeightedFile() {} + + WeightedFile(StringRef F, uint64_t W) : Filename{F}, Weight{W} {} +}; +typedef SmallVector WeightedFileVector; + +static void mergeInstrProfile(const WeightedFileVector &Inputs, StringRef OutputFilename, ProfileFormat OutputFormat) { if (OutputFilename.compare("-") == 0) @@ -107,21 +121,21 @@ InstrProfWriter Writer; SmallSet WriterErrorCodes; - for (const auto &Filename : Inputs) { - auto ReaderOrErr = InstrProfReader::create(Filename); + for (const auto &Input : Inputs) { + auto ReaderOrErr = InstrProfReader::create(Input.Filename); if (std::error_code ec = ReaderOrErr.getError()) - exitWithErrorCode(ec, Filename); + exitWithErrorCode(ec, Input.Filename); auto Reader = std::move(ReaderOrErr.get()); for (auto &I : *Reader) { - if (std::error_code EC = Writer.addRecord(std::move(I))) { + if (std::error_code EC = Writer.addRecord(std::move(I), Input.Weight)) { // Only show hint the first time an error occurs. bool firstTime = WriterErrorCodes.insert(EC).second; - handleMergeWriterError(EC, Filename, I.Name, firstTime); + handleMergeWriterError(EC, Input.Filename, I.Name, firstTime); } } if (Reader->hasError()) - exitWithErrorCode(Reader->getError(), Filename); + exitWithErrorCode(Reader->getError(), Input.Filename); } if (OutputFormat == PF_Text) Writer.writeText(Output); @@ -133,7 +147,7 @@ sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Binary, sampleprof::SPF_GCC}; -static void mergeSampleProfile(const cl::list &Inputs, +static void mergeSampleProfile(const WeightedFileVector &Inputs, StringRef OutputFilename, ProfileFormat OutputFormat) { using namespace sampleprof; @@ -145,11 +159,11 @@ auto Writer = std::move(WriterOrErr.get()); StringMap ProfileMap; SmallVector, 5> Readers; - for (const auto &Filename : Inputs) { + for (const auto &Input : Inputs) { auto ReaderOrErr = - SampleProfileReader::create(Filename, getGlobalContext()); + SampleProfileReader::create(Input.Filename, getGlobalContext()); if (std::error_code EC = ReaderOrErr.getError()) - exitWithErrorCode(EC, Filename); + exitWithErrorCode(EC, Input.Filename); // We need to keep the readers around until after all the files are // read so that we do not lose the function names stored in each @@ -158,7 +172,7 @@ Readers.push_back(std::move(ReaderOrErr.get())); const auto Reader = Readers.back().get(); if (std::error_code EC = Reader->read()) - exitWithErrorCode(EC, Filename); + exitWithErrorCode(EC, Input.Filename); StringMap &Profiles = Reader->getProfiles(); for (StringMap::iterator I = Profiles.begin(), @@ -166,16 +180,32 @@ I != E; ++I) { StringRef FName = I->first(); FunctionSamples &Samples = I->second; - ProfileMap[FName].merge(Samples); + ProfileMap[FName].merge(Samples, Input.Weight); } } Writer->write(ProfileMap); } -static int merge_main(int argc, const char *argv[]) { - cl::list Inputs(cl::Positional, cl::Required, cl::OneOrMore, - cl::desc("")); +static WeightedFile parseWeightedFile(const StringRef &WeightedFilename) { + StringRef WeightStr, FileName; + std::tie(WeightStr, FileName) = WeightedFilename.split(','); + + uint64_t Weight; + if (WeightStr.getAsInteger(10, Weight) || Weight < 1) + exitWithError("Input weight must be a positive integer."); + + if (!sys::fs::exists(FileName)) + exitWithErrorCode(make_error_code(errc::no_such_file_or_directory), + FileName); + return WeightedFile(FileName, Weight); +} + +static int merge_main(int argc, const char *argv[]) { + cl::list InputFilenames(cl::Positional, + cl::desc("")); + cl::list WeightedInputFilenames("weighted-input", + cl::desc(",")); cl::opt OutputFilename("output", cl::value_desc("output"), cl::init("-"), cl::Required, cl::desc("Output file")); @@ -196,10 +226,20 @@ cl::ParseCommandLineOptions(argc, argv, "LLVM profile data merger\n"); + if (InputFilenames.empty() && WeightedInputFilenames.empty()) + exitWithError("No input files specified. See " + + sys::path::filename(argv[0]) + " -help"); + + WeightedFileVector WeightedInputs; + for (StringRef Filename : InputFilenames) + WeightedInputs.push_back(WeightedFile(Filename, 1)); + for (StringRef WeightedFilename : WeightedInputFilenames) + WeightedInputs.push_back(parseWeightedFile(WeightedFilename)); + if (ProfileKind == instr) - mergeInstrProfile(Inputs, OutputFilename, OutputFormat); + mergeInstrProfile(WeightedInputs, OutputFilename, OutputFormat); else - mergeSampleProfile(Inputs, OutputFilename, OutputFormat); + mergeSampleProfile(WeightedInputs, OutputFilename, OutputFormat); return 0; } Index: llvm/trunk/unittests/ProfileData/InstrProfTest.cpp =================================================================== --- llvm/trunk/unittests/ProfileData/InstrProfTest.cpp +++ llvm/trunk/unittests/ProfileData/InstrProfTest.cpp @@ -362,7 +362,9 @@ Record1.addValueData(IPVK_IndirectCallTarget, 0, VD1, 1, nullptr); Record2.reserveSites(IPVK_IndirectCallTarget, 1); - InstrProfValueData VD2[] = {{(uint64_t) "callee1", Max}}; + // FIXME: Improve handling of counter overflow. ValueData asserts on overflow. + // InstrProfValueData VD2[] = {{(uint64_t) "callee1", Max}}; + InstrProfValueData VD2[] = {{(uint64_t) "callee1", 1}}; Record2.addValueData(IPVK_IndirectCallTarget, 0, VD2, 1, nullptr); Writer.addRecord(std::move(Record1)); @@ -382,7 +384,10 @@ std::unique_ptr VD = R.get().getValueForSite(IPVK_IndirectCallTarget, 0); ASSERT_EQ(StringRef("callee1"), StringRef((const char *)VD[0].Value, 7)); - ASSERT_EQ(Max, VD[0].Count); + + // FIXME: Improve handling of counter overflow. ValueData asserts on overflow. + // ASSERT_EQ(Max, VD[0].Count); + ASSERT_EQ(2U, VD[0].Count); } // Synthesize runtime value profile data. @@ -490,4 +495,24 @@ ASSERT_EQ(1ULL << 63, Reader->getMaximumFunctionCount()); } +TEST_F(InstrProfTest, get_weighted_function_counts) { + InstrProfRecord Record1("foo", 0x1234, {1, 2}); + InstrProfRecord Record2("foo", 0x1235, {3, 4}); + Writer.addRecord(std::move(Record1), 3); + Writer.addRecord(std::move(Record2), 5); + auto Profile = Writer.writeBuffer(); + readProfile(std::move(Profile)); + + std::vector Counts; + ASSERT_TRUE(NoError(Reader->getFunctionCounts("foo", 0x1234, Counts))); + ASSERT_EQ(2U, Counts.size()); + ASSERT_EQ(3U, Counts[0]); + ASSERT_EQ(6U, Counts[1]); + + ASSERT_TRUE(NoError(Reader->getFunctionCounts("foo", 0x1235, Counts))); + ASSERT_EQ(2U, Counts.size()); + ASSERT_EQ(15U, Counts[0]); + ASSERT_EQ(20U, Counts[1]); +} + } // end anonymous namespace