1 //===- bolt/Rewrite/BoltDiff.cpp ------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // RewriteInstance methods related to comparing one instance to another, used
10 // by the boltdiff tool to print a report.
12 //===----------------------------------------------------------------------===//
14 #include "bolt/Passes/IdenticalCodeFolding.h"
15 #include "bolt/Profile/ProfileReaderBase.h"
16 #include "bolt/Rewrite/RewriteInstance.h"
17 #include "bolt/Utils/Utils.h"
18 #include "llvm/ADT/STLExtras.h"
19 #include "llvm/Support/CommandLine.h"
22 #define DEBUG_TYPE "boltdiff"
25 using namespace object
;
29 extern cl::OptionCategory BoltDiffCategory
;
30 extern cl::opt
<bool> NeverPrint
;
31 extern cl::opt
<bool> ICF
;
33 static cl::opt
<bool> IgnoreLTOSuffix(
35 cl::desc("ignore lto_priv or const suffixes when matching functions"),
36 cl::init(true), cl::cat(BoltDiffCategory
));
38 static cl::opt
<bool> PrintUnmapped(
40 cl::desc("print functions of binary 2 that were not matched to any "
41 "function in binary 1"),
42 cl::cat(BoltDiffCategory
));
44 static cl::opt
<bool> PrintProfiledUnmapped(
45 "print-profiled-unmapped",
46 cl::desc("print functions that have profile in binary 1 but do not "
48 cl::cat(BoltDiffCategory
));
50 static cl::opt
<bool> PrintDiffCFG(
52 cl::desc("print the CFG of important functions that changed in "
54 cl::cat(BoltDiffCategory
));
57 PrintDiffBBs("print-diff-bbs",
58 cl::desc("print the basic blocks showed in top differences"),
59 cl::cat(BoltDiffCategory
));
61 static cl::opt
<bool> MatchByHash(
63 cl::desc("match functions in binary 2 to binary 1 if they have the same "
64 "hash of a function in binary 1"),
65 cl::cat(BoltDiffCategory
));
67 static cl::opt
<bool> IgnoreUnchanged(
69 cl::desc("do not diff functions whose contents have not been changed from "
70 "one binary to another"),
71 cl::cat(BoltDiffCategory
));
73 static cl::opt
<unsigned> DisplayCount(
75 cl::desc("number of functions to display when printing the top largest "
76 "differences in function activity"),
77 cl::init(10), cl::cat(BoltDiffCategory
));
79 static cl::opt
<bool> NormalizeByBin1(
81 cl::desc("show execution count of functions in binary 2 as a ratio of the "
82 "total samples in binary 1 - make sure both profiles have equal "
83 "collection time and sampling rate for this to make sense"),
84 cl::cat(BoltDiffCategory
));
87 SkipNonSimple("skip-non-simple",
88 cl::desc("skip non-simple functions in reporting"),
89 cl::ReallyHidden
, cl::cat(BoltDiffCategory
));
91 } // end namespace opts
98 /// Helper used to print colored numbers
99 void printColoredPercentage(double Perc
) {
100 if (outs().has_colors() && Perc
> 0.0)
101 outs().changeColor(raw_ostream::RED
);
102 else if (outs().has_colors() && Perc
< 0.0)
103 outs().changeColor(raw_ostream::GREEN
);
104 else if (outs().has_colors())
105 outs().changeColor(raw_ostream::YELLOW
);
106 outs() << format("%.2f", Perc
) << "%";
107 if (outs().has_colors())
111 void setLightColor() {
112 if (opts::PrintDiffBBs
&& outs().has_colors())
113 outs().changeColor(raw_ostream::CYAN
);
116 void setTitleColor() {
117 if (outs().has_colors())
118 outs().changeColor(raw_ostream::WHITE
, /*Bold=*/true);
121 void setRegularColor() {
122 if (outs().has_colors())
126 } // end anonymous namespace
128 /// Perform the comparison between two binaries with profiling information
129 class RewriteInstanceDiff
{
130 typedef std::tuple
<const BinaryBasicBlock
*, const BinaryBasicBlock
*, double>
133 RewriteInstance
&RI1
;
134 RewriteInstance
&RI2
;
136 // The map of functions keyed by functions in binary 2, providing its
137 // corresponding function in binary 1
138 std::map
<const BinaryFunction
*, const BinaryFunction
*> FuncMap
;
140 // The map of basic blocks correspondence, analogue to FuncMap for BBs,
141 // sorted by score difference
142 std::map
<const BinaryBasicBlock
*, const BinaryBasicBlock
*> BBMap
;
144 // The map of edge correspondence
145 std::map
<double, std::pair
<EdgeTy
, EdgeTy
>> EdgeMap
;
147 // Maps all known basic blocks back to their parent function
148 std::map
<const BinaryBasicBlock
*, const BinaryFunction
*> BBToFuncMap
;
150 // Accounting which functions were matched
151 std::set
<const BinaryFunction
*> Bin1MappedFuncs
;
152 std::set
<const BinaryFunction
*> Bin2MappedFuncs
;
154 // Structures for our 3 matching strategies: by name, by hash and by lto name,
155 // from the strongest to the weakest bind between two functions
156 StringMap
<const BinaryFunction
*> NameLookup
;
157 DenseMap
<size_t, const BinaryFunction
*> HashLookup
;
158 StringMap
<const BinaryFunction
*> LTONameLookup1
;
159 StringMap
<const BinaryFunction
*> LTONameLookup2
;
161 // Score maps used to order and find hottest functions
162 std::multimap
<double, const BinaryFunction
*> LargestBin1
;
163 std::multimap
<double, const BinaryFunction
*> LargestBin2
;
165 // Map multiple functions in the same LTO bucket to a single parent function
166 // representing all functions sharing the same prefix
167 std::map
<const BinaryFunction
*, const BinaryFunction
*> LTOMap1
;
168 std::map
<const BinaryFunction
*, const BinaryFunction
*> LTOMap2
;
169 std::map
<const BinaryFunction
*, double> LTOAggregatedScore1
;
170 std::map
<const BinaryFunction
*, double> LTOAggregatedScore2
;
172 // Map scores in bin2 and 1 keyed by a binary 2 function - post-matching
173 DenseMap
<const BinaryFunction
*, std::pair
<double, double>> ScoreMap
;
175 double getNormalizedScore(const BinaryFunction
&Function
,
176 const RewriteInstance
&Ctx
) {
177 if (!opts::NormalizeByBin1
)
178 return static_cast<double>(Function
.getFunctionScore()) /
180 return static_cast<double>(Function
.getFunctionScore()) /
184 double getNormalizedScore(const BinaryBasicBlock
&BB
,
185 const RewriteInstance
&Ctx
) {
186 if (!opts::NormalizeByBin1
)
187 return static_cast<double>(BB
.getKnownExecutionCount()) /
189 return static_cast<double>(BB
.getKnownExecutionCount()) /
193 double getNormalizedScore(BinaryBasicBlock::const_branch_info_iterator BIIter
,
194 const RewriteInstance
&Ctx
) {
196 BIIter
->Count
== BinaryBasicBlock::COUNT_NO_PROFILE
? 0 : BIIter
->Count
;
197 if (!opts::NormalizeByBin1
)
198 return Score
/ Ctx
.getTotalScore();
199 return Score
/ RI1
.getTotalScore();
202 /// Initialize data structures used for function lookup in binary 1, used
203 /// later when matching functions in binary 2 to corresponding functions
205 void buildLookupMaps() {
206 for (const auto &BFI
: RI1
.BC
->getBinaryFunctions()) {
208 const BinaryFunction
&Function
= BFI
.second
;
209 const double Score
= getNormalizedScore(Function
, RI1
);
210 LargestBin1
.insert(std::make_pair
<>(Score
, &Function
));
211 for (const StringRef
&Name
: Function
.getNames()) {
212 if (std::optional
<StringRef
> OptionalLTOName
= getLTOCommonName(Name
))
213 LTOName
= *OptionalLTOName
;
214 NameLookup
[Name
] = &Function
;
216 if (opts::MatchByHash
&& Function
.hasCFG())
217 HashLookup
[Function
.computeHash(/*UseDFS=*/true)] = &Function
;
218 if (opts::IgnoreLTOSuffix
&& !LTOName
.empty()) {
219 if (!LTONameLookup1
.count(LTOName
))
220 LTONameLookup1
[LTOName
] = &Function
;
221 LTOMap1
[&Function
] = LTONameLookup1
[LTOName
];
225 // Compute LTONameLookup2 and LargestBin2
226 for (const auto &BFI
: RI2
.BC
->getBinaryFunctions()) {
228 const BinaryFunction
&Function
= BFI
.second
;
229 const double Score
= getNormalizedScore(Function
, RI2
);
230 LargestBin2
.insert(std::make_pair
<>(Score
, &Function
));
231 for (const StringRef
&Name
: Function
.getNames()) {
232 if (std::optional
<StringRef
> OptionalLTOName
= getLTOCommonName(Name
))
233 LTOName
= *OptionalLTOName
;
235 if (opts::IgnoreLTOSuffix
&& !LTOName
.empty()) {
236 if (!LTONameLookup2
.count(LTOName
))
237 LTONameLookup2
[LTOName
] = &Function
;
238 LTOMap2
[&Function
] = LTONameLookup2
[LTOName
];
243 /// Match functions in binary 2 with functions in binary 1
244 void matchFunctions() {
245 outs() << "BOLT-DIFF: Mapping functions in Binary2 to Binary1\n";
246 uint64_t BothHaveProfile
= 0ull;
247 std::set
<const BinaryFunction
*> Bin1ProfiledMapped
;
249 for (const auto &BFI2
: RI2
.BC
->getBinaryFunctions()) {
250 const BinaryFunction
&Function2
= BFI2
.second
;
253 for (const StringRef
&Name
: Function2
.getNames()) {
254 auto Iter
= NameLookup
.find(Name
);
255 if (std::optional
<StringRef
> OptionalLTOName
= getLTOCommonName(Name
))
256 LTOName
= *OptionalLTOName
;
257 if (Iter
== NameLookup
.end())
259 FuncMap
.insert(std::make_pair
<>(&Function2
, Iter
->second
));
260 Bin1MappedFuncs
.insert(Iter
->second
);
261 Bin2MappedFuncs
.insert(&Function2
);
262 if (Function2
.hasValidProfile() && Iter
->second
->hasValidProfile()) {
264 Bin1ProfiledMapped
.insert(Iter
->second
);
269 if (Match
|| !Function2
.hasCFG())
271 auto Iter
= HashLookup
.find(Function2
.computeHash(/*UseDFS*/ true));
272 if (Iter
!= HashLookup
.end()) {
273 FuncMap
.insert(std::make_pair
<>(&Function2
, Iter
->second
));
274 Bin1MappedFuncs
.insert(Iter
->second
);
275 Bin2MappedFuncs
.insert(&Function2
);
276 if (Function2
.hasValidProfile() && Iter
->second
->hasValidProfile()) {
278 Bin1ProfiledMapped
.insert(Iter
->second
);
284 auto LTOIter
= LTONameLookup1
.find(LTOName
);
285 if (LTOIter
!= LTONameLookup1
.end()) {
286 FuncMap
.insert(std::make_pair
<>(&Function2
, LTOIter
->second
));
287 Bin1MappedFuncs
.insert(LTOIter
->second
);
288 Bin2MappedFuncs
.insert(&Function2
);
289 if (Function2
.hasValidProfile() && LTOIter
->second
->hasValidProfile()) {
291 Bin1ProfiledMapped
.insert(LTOIter
->second
);
295 PrintProgramStats
PPS(opts::NeverPrint
);
296 outs() << "* BOLT-DIFF: Starting print program stats pass for binary 1\n";
297 PPS
.runOnFunctions(*RI1
.BC
);
298 outs() << "* BOLT-DIFF: Starting print program stats pass for binary 2\n";
299 PPS
.runOnFunctions(*RI2
.BC
);
301 outs() << "Inputs share " << BothHaveProfile
302 << " functions with valid profile.\n";
303 if (opts::PrintProfiledUnmapped
) {
304 outs() << "\nFunctions in profile 1 that are missing in the profile 2:\n";
305 std::vector
<const BinaryFunction
*> Unmapped
;
306 for (const auto &BFI
: RI1
.BC
->getBinaryFunctions()) {
307 const BinaryFunction
&Function
= BFI
.second
;
308 if (!Function
.hasValidProfile() || Bin1ProfiledMapped
.count(&Function
))
310 Unmapped
.emplace_back(&Function
);
313 [&](const BinaryFunction
*A
, const BinaryFunction
*B
) {
314 return A
->getFunctionScore() > B
->getFunctionScore();
316 for (const BinaryFunction
*Function
: Unmapped
) {
317 outs() << Function
->getPrintName() << " : ";
318 outs() << Function
->getFunctionScore() << "\n";
324 /// Check if opcodes in BB1 match those in BB2
325 bool compareBBs(const BinaryBasicBlock
&BB1
,
326 const BinaryBasicBlock
&BB2
) const {
327 auto Iter1
= BB1
.begin();
328 auto Iter2
= BB2
.begin();
329 if ((Iter1
== BB1
.end() && Iter2
!= BB2
.end()) ||
330 (Iter1
!= BB1
.end() && Iter2
== BB2
.end()))
333 while (Iter1
!= BB1
.end()) {
334 if (Iter2
== BB2
.end() || Iter1
->getOpcode() != Iter2
->getOpcode())
341 if (Iter2
!= BB2
.end())
346 /// For a function in binary 2 that matched one in binary 1, now match each
347 /// individual basic block in it to its corresponding blocks in binary 1.
348 /// Also match each edge in binary 2 to the corresponding ones in binary 1.
349 void matchBasicBlocks() {
350 for (const auto &MapEntry
: FuncMap
) {
351 const BinaryFunction
*const &Func1
= MapEntry
.second
;
352 const BinaryFunction
*const &Func2
= MapEntry
.first
;
354 auto Iter1
= Func1
->getLayout().block_begin();
355 auto Iter2
= Func2
->getLayout().block_begin();
358 std::map
<const BinaryBasicBlock
*, const BinaryBasicBlock
*> Map
;
359 std::map
<double, std::pair
<EdgeTy
, EdgeTy
>> EMap
;
360 while (Iter1
!= Func1
->getLayout().block_end()) {
361 if (Iter2
== Func2
->getLayout().block_end()) {
365 if (!compareBBs(**Iter1
, **Iter2
)) {
369 Map
.insert(std::make_pair
<>(*Iter2
, *Iter1
));
371 auto SuccIter1
= (*Iter1
)->succ_begin();
372 auto SuccIter2
= (*Iter2
)->succ_begin();
373 auto BIIter1
= (*Iter1
)->branch_info_begin();
374 auto BIIter2
= (*Iter2
)->branch_info_begin();
375 while (SuccIter1
!= (*Iter1
)->succ_end()) {
376 if (SuccIter2
== (*Iter2
)->succ_end()) {
380 const double ScoreEdge1
= getNormalizedScore(BIIter1
, RI1
);
381 const double ScoreEdge2
= getNormalizedScore(BIIter2
, RI2
);
382 EMap
.insert(std::make_pair
<>(
383 std::abs(ScoreEdge2
- ScoreEdge1
),
385 std::make_tuple
<>(*Iter2
, *SuccIter2
, ScoreEdge2
),
386 std::make_tuple
<>(*Iter1
, *SuccIter1
, ScoreEdge1
))));
393 if (SuccIter2
!= (*Iter2
)->succ_end())
398 BBToFuncMap
[*Iter1
] = Func1
;
399 BBToFuncMap
[*Iter2
] = Func2
;
403 if (!Match
|| Iter2
!= Func2
->getLayout().block_end())
406 BBMap
.insert(Map
.begin(), Map
.end());
407 EdgeMap
.insert(EMap
.begin(), EMap
.end());
411 /// Print the largest differences in basic block performance from binary 1
413 void reportHottestBBDiffs() {
414 std::map
<double, const BinaryBasicBlock
*> LargestDiffs
;
415 for (const auto &MapEntry
: BBMap
) {
416 const BinaryBasicBlock
*BB2
= MapEntry
.first
;
417 const BinaryBasicBlock
*BB1
= MapEntry
.second
;
419 std::make_pair
<>(std::abs(getNormalizedScore(*BB2
, RI2
) -
420 getNormalizedScore(*BB1
, RI1
)),
424 unsigned Printed
= 0;
427 << "\nTop " << opts::DisplayCount
428 << " largest differences in basic block performance bin 2 -> bin 1:\n";
429 outs() << "=========================================================\n";
431 outs() << " * Functions with different contents do not appear here\n\n";
432 for (const BinaryBasicBlock
*BB2
:
433 llvm::make_second_range(llvm::reverse(LargestDiffs
))) {
434 const double Score2
= getNormalizedScore(*BB2
, RI2
);
435 const double Score1
= getNormalizedScore(*BBMap
[BB2
], RI1
);
436 const BinaryFunction
*Func
= BBToFuncMap
[BB2
];
437 if (opts::SkipNonSimple
&& !Func
->isSimple())
439 outs() << "BB " << BB2
->getName() << " from " << Func
->getDemangledName()
440 << "\n\tScore bin1 = " << format("%.4f", Score1
* 100.0)
441 << "%\n\tScore bin2 = " << format("%.4f", Score2
* 100.0);
442 outs() << "%\t(Difference: ";
443 printColoredPercentage((Score2
- Score1
) * 100.0);
445 if (opts::PrintDiffBBs
) {
450 if (Printed
++ == opts::DisplayCount
)
455 /// Print the largest differences in edge counts from one binary to another
456 void reportHottestEdgeDiffs() {
457 unsigned Printed
= 0;
459 outs() << "\nTop " << opts::DisplayCount
460 << " largest differences in edge hotness bin 2 -> bin 1:\n";
461 outs() << "=========================================================\n";
463 outs() << " * Functions with different contents do not appear here\n";
464 for (std::pair
<EdgeTy
, EdgeTy
> &EI
:
465 llvm::make_second_range(llvm::reverse(EdgeMap
))) {
466 EdgeTy
&Edge2
= EI
.first
;
467 EdgeTy
&Edge1
= EI
.second
;
468 const double Score2
= std::get
<2>(Edge2
);
469 const double Score1
= std::get
<2>(Edge1
);
470 const BinaryFunction
*Func
= BBToFuncMap
[std::get
<0>(Edge2
)];
471 if (opts::SkipNonSimple
&& !Func
->isSimple())
473 outs() << "Edge (" << std::get
<0>(Edge2
)->getName() << " -> "
474 << std::get
<1>(Edge2
)->getName() << ") in "
475 << Func
->getDemangledName()
476 << "\n\tScore bin1 = " << format("%.4f", Score1
* 100.0)
477 << "%\n\tScore bin2 = " << format("%.4f", Score2
* 100.0);
478 outs() << "%\t(Difference: ";
479 printColoredPercentage((Score2
- Score1
) * 100.0);
481 if (opts::PrintDiffBBs
) {
483 std::get
<0>(Edge2
)->dump();
484 std::get
<1>(Edge2
)->dump();
487 if (Printed
++ == opts::DisplayCount
)
492 /// For LTO functions sharing the same prefix (for example, func1.lto_priv.1
493 /// and func1.lto_priv.2 share the func1.lto_priv prefix), compute aggregated
494 /// scores for them. This is used to avoid reporting all LTO functions as
495 /// having a large difference in performance because hotness shifted from
496 /// LTO variant 1 to variant 2, even though they represent the same function.
497 void computeAggregatedLTOScore() {
498 for (const auto &BFI
: RI1
.BC
->getBinaryFunctions()) {
499 const BinaryFunction
&Function
= BFI
.second
;
500 double Score
= getNormalizedScore(Function
, RI1
);
501 auto Iter
= LTOMap1
.find(&Function
);
502 if (Iter
== LTOMap1
.end())
504 LTOAggregatedScore1
[Iter
->second
] += Score
;
507 double UnmappedScore
= 0;
508 for (const auto &BFI
: RI2
.BC
->getBinaryFunctions()) {
509 const BinaryFunction
&Function
= BFI
.second
;
510 bool Matched
= FuncMap
.find(&Function
) != FuncMap
.end();
511 double Score
= getNormalizedScore(Function
, RI2
);
512 auto Iter
= LTOMap2
.find(&Function
);
513 if (Iter
== LTOMap2
.end()) {
515 UnmappedScore
+= Score
;
518 LTOAggregatedScore2
[Iter
->second
] += Score
;
519 if (FuncMap
.find(Iter
->second
) == FuncMap
.end())
520 UnmappedScore
+= Score
;
523 RI2
.BC
->getBinaryFunctions().size() - Bin2MappedFuncs
.size();
524 outs() << "BOLT-DIFF: " << Unmapped
525 << " functions in Binary2 have no correspondence to any other "
526 "function in Binary1.\n";
528 // Print the hotness score of functions in binary 2 that were not matched
529 // to any function in binary 1
530 outs() << "BOLT-DIFF: These unmapped functions in Binary2 represent "
531 << format("%.2f", UnmappedScore
* 100.0) << "% of execution.\n";
534 /// Print the largest hotness differences from binary 2 to binary 1
535 void reportHottestFuncDiffs() {
536 std::multimap
<double, decltype(FuncMap
)::value_type
> LargestDiffs
;
537 for (const auto &MapEntry
: FuncMap
) {
538 const BinaryFunction
*const &Func1
= MapEntry
.second
;
539 const BinaryFunction
*const &Func2
= MapEntry
.first
;
540 double Score1
= getNormalizedScore(*Func1
, RI1
);
541 auto Iter1
= LTOMap1
.find(Func1
);
542 if (Iter1
!= LTOMap1
.end())
543 Score1
= LTOAggregatedScore1
[Iter1
->second
];
544 double Score2
= getNormalizedScore(*Func2
, RI2
);
545 auto Iter2
= LTOMap2
.find(Func2
);
546 if (Iter2
!= LTOMap2
.end())
547 Score2
= LTOAggregatedScore2
[Iter2
->second
];
548 if (Score1
== 0.0 || Score2
== 0.0)
550 if (opts::SkipNonSimple
&& !Func1
->isSimple() && !Func2
->isSimple())
553 std::make_pair
<>(std::abs(Score1
- Score2
), MapEntry
));
554 ScoreMap
[Func2
] = std::make_pair
<>(Score1
, Score2
);
557 unsigned Printed
= 0;
559 outs() << "\nTop " << opts::DisplayCount
560 << " largest differences in performance bin 2 -> bin 1:\n";
561 outs() << "=========================================================\n";
563 for (decltype(this->FuncMap
)::value_type
&MapEntry
:
564 llvm::make_second_range(llvm::reverse(LargestDiffs
))) {
565 if (opts::IgnoreUnchanged
&&
566 MapEntry
.second
->computeHash(/*UseDFS=*/true) ==
567 MapEntry
.first
->computeHash(/*UseDFS=*/true))
569 const std::pair
<double, double> &Scores
= ScoreMap
[MapEntry
.first
];
570 outs() << "Function " << MapEntry
.first
->getDemangledName();
571 if (MapEntry
.first
->getDemangledName() !=
572 MapEntry
.second
->getDemangledName())
573 outs() << "\nmatched " << MapEntry
.second
->getDemangledName();
574 outs() << "\n\tScore bin1 = " << format("%.2f", Scores
.first
* 100.0)
575 << "%\n\tScore bin2 = " << format("%.2f", Scores
.second
* 100.0)
576 << "%\t(Difference: ";
577 printColoredPercentage((Scores
.second
- Scores
.first
) * 100.0);
579 if (MapEntry
.second
->computeHash(/*UseDFS=*/true) !=
580 MapEntry
.first
->computeHash(/*UseDFS=*/true)) {
581 outs() << "\t[Functions have different contents]";
582 if (opts::PrintDiffCFG
) {
583 outs() << "\n *** CFG for function in binary 1:\n";
585 MapEntry
.second
->dump();
587 outs() << "\n *** CFG for function in binary 2:\n";
589 MapEntry
.first
->dump();
594 if (Printed
++ == opts::DisplayCount
)
599 /// Print hottest functions from each binary
600 void reportHottestFuncs() {
601 unsigned Printed
= 0;
603 outs() << "\nTop " << opts::DisplayCount
604 << " hottest functions in binary 2:\n";
605 outs() << "=====================================\n";
607 for (std::pair
<const double, const BinaryFunction
*> &MapEntry
:
608 llvm::reverse(LargestBin2
)) {
609 outs() << "Function " << MapEntry
.second
->getDemangledName() << "\n";
610 auto Iter
= ScoreMap
.find(MapEntry
.second
);
611 if (Iter
!= ScoreMap
.end())
612 outs() << "\tScore bin1 = "
613 << format("%.2f", Iter
->second
.first
* 100.0) << "%\n";
614 outs() << "\tScore bin2 = " << format("%.2f", MapEntry
.first
* 100.0)
616 if (Printed
++ == opts::DisplayCount
)
622 outs() << "\nTop " << opts::DisplayCount
623 << " hottest functions in binary 1:\n";
624 outs() << "=====================================\n";
626 for (const std::pair
<const double, const BinaryFunction
*> &MapEntry
:
627 llvm::reverse(LargestBin1
)) {
628 outs() << "Function " << MapEntry
.second
->getDemangledName()
629 << "\n\tScore bin1 = " << format("%.2f", MapEntry
.first
* 100.0)
631 if (Printed
++ == opts::DisplayCount
)
636 /// Print functions in binary 2 that did not match anything in binary 1.
637 /// Unfortunately, in an LTO build, even a small change can lead to several
638 /// LTO variants being unmapped, corresponding to local functions that never
639 /// appear in one of the binaries because they were previously inlined.
640 void reportUnmapped() {
641 outs() << "List of functions from binary 2 that were not matched with any "
642 << "function in binary 1:\n";
643 for (const auto &BFI2
: RI2
.BC
->getBinaryFunctions()) {
644 const BinaryFunction
&Function2
= BFI2
.second
;
645 if (Bin2MappedFuncs
.count(&Function2
))
647 outs() << Function2
.getPrintName() << "\n";
652 /// Main entry point: coordinate all tasks necessary to compare two binaries
653 void compareAndReport() {
656 if (opts::IgnoreLTOSuffix
)
657 computeAggregatedLTOScore();
659 reportHottestFuncDiffs();
660 reportHottestBBDiffs();
661 reportHottestEdgeDiffs();
662 reportHottestFuncs();
663 if (!opts::PrintUnmapped
)
668 RewriteInstanceDiff(RewriteInstance
&RI1
, RewriteInstance
&RI2
)
669 : RI1(RI1
), RI2(RI2
) {
675 } // end nampespace bolt
676 } // end namespace llvm
678 void RewriteInstance::compare(RewriteInstance
&RI2
) {
679 outs() << "BOLT-DIFF: ======== Binary1 vs. Binary2 ========\n";
680 outs() << "Trace for binary 1 has " << this->getTotalScore()
681 << " instructions executed.\n";
682 outs() << "Trace for binary 2 has " << RI2
.getTotalScore()
683 << " instructions executed.\n";
684 if (opts::NormalizeByBin1
) {
686 static_cast<double>(RI2
.getTotalScore() - this->getTotalScore()) /
687 this->getTotalScore();
688 outs() << "Binary2 change in score with respect to Binary1: ";
689 printColoredPercentage(Diff2to1
* 100.0);
693 if (!this->getTotalScore() || !RI2
.getTotalScore()) {
694 outs() << "BOLT-DIFF: Both binaries must have recorded activity in known "
701 IdenticalCodeFolding
ICF(opts::NeverPrint
);
702 outs() << "BOLT-DIFF: Starting ICF pass for binary 1";
703 ICF
.runOnFunctions(*BC
);
704 outs() << "BOLT-DIFF: Starting ICF pass for binary 2";
705 ICF
.runOnFunctions(*RI2
.BC
);
708 RewriteInstanceDiff
RID(*this, RI2
);