xref: /llvm-project/bolt/lib/Rewrite/BoltDiff.cpp (revision f40d25dd8d3ad7bcfa8f5e8f74f245ab1a7675df)
1 //===- bolt/Rewrite/BoltDiff.cpp ------------------------------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // RewriteInstance methods related to comparing one instance to another, used
10 // by the boltdiff tool to print a report.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "bolt/Passes/IdenticalCodeFolding.h"
15 #include "bolt/Profile/ProfileReaderBase.h"
16 #include "bolt/Rewrite/RewriteInstance.h"
17 #include "llvm/ADT/STLExtras.h"
18 #include "llvm/Support/CommandLine.h"
19 
20 #undef  DEBUG_TYPE
21 #define DEBUG_TYPE "boltdiff"
22 
23 using namespace llvm;
24 using namespace object;
25 using namespace bolt;
26 
27 namespace opts {
28 extern cl::OptionCategory BoltDiffCategory;
29 extern cl::opt<bool> NeverPrint;
30 extern cl::opt<bool> ICF;
31 
32 static cl::opt<bool> IgnoreLTOSuffix(
33     "ignore-lto-suffix",
34     cl::desc("ignore lto_priv or const suffixes when matching functions"),
35     cl::init(true), cl::cat(BoltDiffCategory));
36 
37 static cl::opt<bool> PrintUnmapped(
38     "print-unmapped",
39     cl::desc("print functions of binary 2 that were not matched to any "
40              "function in binary 1"),
41     cl::cat(BoltDiffCategory));
42 
43 static cl::opt<bool> PrintProfiledUnmapped(
44     "print-profiled-unmapped",
45     cl::desc("print functions that have profile in binary 1 but do not "
46              "in binary 2"),
47     cl::cat(BoltDiffCategory));
48 
49 static cl::opt<bool> PrintDiffCFG(
50     "print-diff-cfg",
51     cl::desc("print the CFG of important functions that changed in "
52              "binary 2"),
53     cl::cat(BoltDiffCategory));
54 
55 static cl::opt<bool>
56     PrintDiffBBs("print-diff-bbs",
57                  cl::desc("print the basic blocks showed in top differences"),
58                  cl::cat(BoltDiffCategory));
59 
60 static cl::opt<bool> MatchByHash(
61     "match-by-hash",
62     cl::desc("match functions in binary 2 to binary 1 if they have the same "
63              "hash of a function in binary 1"),
64     cl::cat(BoltDiffCategory));
65 
66 static cl::opt<bool> IgnoreUnchanged(
67     "ignore-unchanged",
68     cl::desc("do not diff functions whose contents have not been changed from "
69              "one binary to another"),
70     cl::cat(BoltDiffCategory));
71 
72 static cl::opt<unsigned> DisplayCount(
73     "display-count",
74     cl::desc("number of functions to display when printing the top largest "
75              "differences in function activity"),
76     cl::init(10), cl::cat(BoltDiffCategory));
77 
78 static cl::opt<bool> NormalizeByBin1(
79     "normalize-by-bin1",
80     cl::desc("show execution count of functions in binary 2 as a ratio of the "
81              "total samples in binary 1 - make sure both profiles have equal "
82              "collection time and sampling rate for this to make sense"),
83     cl::cat(BoltDiffCategory));
84 
85 } // end namespace opts
86 
87 namespace llvm {
88 namespace bolt {
89 
90 namespace {
91 
92 /// Helper used to print colored numbers
93 void printColoredPercentage(double Perc) {
94   if (outs().has_colors() && Perc > 0.0)
95     outs().changeColor(raw_ostream::RED);
96   else if (outs().has_colors() && Perc < 0.0)
97     outs().changeColor(raw_ostream::GREEN);
98   else if (outs().has_colors())
99     outs().changeColor(raw_ostream::YELLOW);
100   outs() << format("%.2f", Perc) << "%";
101   if (outs().has_colors())
102     outs().resetColor();
103 }
104 
105 void setLightColor() {
106   if (opts::PrintDiffBBs && outs().has_colors())
107     outs().changeColor(raw_ostream::CYAN);
108 }
109 
110 void setTitleColor() {
111   if (outs().has_colors())
112     outs().changeColor(raw_ostream::WHITE, /*Bold=*/true);
113 }
114 
115 void setRegularColor() {
116   if (outs().has_colors())
117     outs().resetColor();
118 }
119 
120 } // end anonymous namespace
121 
122 /// Perform the comparison between two binaries with profiling information
123 class RewriteInstanceDiff {
124   typedef std::tuple<const BinaryBasicBlock *, const BinaryBasicBlock *, double>
125       EdgeTy;
126 
127   RewriteInstance &RI1;
128   RewriteInstance &RI2;
129 
130   // The map of functions keyed by functions in binary 2, providing its
131   // corresponding function in binary 1
132   std::map<const BinaryFunction *, const BinaryFunction *> FuncMap;
133 
134   // The map of basic blocks correspondence, analogue to FuncMap for BBs,
135   // sorted by score difference
136   std::map<const BinaryBasicBlock *, const BinaryBasicBlock *> BBMap;
137 
138   // The map of edge correspondence
139   std::map<double, std::pair<EdgeTy, EdgeTy>> EdgeMap;
140 
141   // Maps all known basic blocks back to their parent function
142   std::map<const BinaryBasicBlock *, const BinaryFunction *> BBToFuncMap;
143 
144   // Accounting which functions were matched
145   std::set<const BinaryFunction *> Bin1MappedFuncs;
146   std::set<const BinaryFunction *> Bin2MappedFuncs;
147 
148   // Structures for our 3 matching strategies: by name, by hash and by lto name,
149   // from the strongest to the weakest bind between two functions
150   StringMap<const BinaryFunction *> NameLookup;
151   DenseMap<size_t, const BinaryFunction *> HashLookup;
152   StringMap<const BinaryFunction *> LTONameLookup1;
153   StringMap<const BinaryFunction *> LTONameLookup2;
154 
155   // Score maps used to order and find hottest functions
156   std::multimap<double, const BinaryFunction *> LargestBin1;
157   std::multimap<double, const BinaryFunction *> LargestBin2;
158 
159   // Map multiple functions in the same LTO bucket to a single parent function
160   // representing all functions sharing the same prefix
161   std::map<const BinaryFunction *, const BinaryFunction *> LTOMap1;
162   std::map<const BinaryFunction *, const BinaryFunction *> LTOMap2;
163   std::map<const BinaryFunction *, double> LTOAggregatedScore1;
164   std::map<const BinaryFunction *, double> LTOAggregatedScore2;
165 
166   // Map scores in bin2 and 1 keyed by a binary 2 function - post-matching
167   DenseMap<const BinaryFunction *, std::pair<double, double>> ScoreMap;
168 
169   double getNormalizedScore(const BinaryFunction &Function,
170                             const RewriteInstance &Ctx) {
171     if (!opts::NormalizeByBin1)
172       return static_cast<double>(Function.getFunctionScore()) /
173              Ctx.getTotalScore();
174     return static_cast<double>(Function.getFunctionScore()) /
175            RI1.getTotalScore();
176   }
177 
178   double getNormalizedScore(const BinaryBasicBlock &BB,
179                             const RewriteInstance &Ctx) {
180     if (!opts::NormalizeByBin1)
181       return static_cast<double>(BB.getKnownExecutionCount()) /
182              Ctx.getTotalScore();
183     return static_cast<double>(BB.getKnownExecutionCount()) /
184            RI1.getTotalScore();
185   }
186 
187   double getNormalizedScore(BinaryBasicBlock::const_branch_info_iterator BIIter,
188                             const RewriteInstance &Ctx) {
189     double Score =
190         BIIter->Count == BinaryBasicBlock::COUNT_NO_PROFILE ? 0 : BIIter->Count;
191     if (!opts::NormalizeByBin1)
192       return Score / Ctx.getTotalScore();
193     return Score / RI1.getTotalScore();
194   }
195 
196   /// Initialize data structures used for function lookup in binary 1, used
197   /// later when matching functions in binary 2 to corresponding functions
198   /// in binary 1
199   void buildLookupMaps() {
200     for (const auto &BFI : RI1.BC->getBinaryFunctions()) {
201       StringRef LTOName;
202       const BinaryFunction &Function = BFI.second;
203       const double Score = getNormalizedScore(Function, RI1);
204       LargestBin1.insert(std::make_pair<>(Score, &Function));
205       for (const StringRef &Name : Function.getNames()) {
206         if (std::optional<StringRef> OptionalLTOName = getLTOCommonName(Name))
207           LTOName = *OptionalLTOName;
208         NameLookup[Name] = &Function;
209       }
210       if (opts::MatchByHash && Function.hasCFG())
211         HashLookup[Function.computeHash(/*UseDFS=*/true)] = &Function;
212       if (opts::IgnoreLTOSuffix && !LTOName.empty()) {
213         if (!LTONameLookup1.count(LTOName))
214           LTONameLookup1[LTOName] = &Function;
215         LTOMap1[&Function] = LTONameLookup1[LTOName];
216       }
217     }
218 
219     // Compute LTONameLookup2 and LargestBin2
220     for (const auto &BFI : RI2.BC->getBinaryFunctions()) {
221       StringRef LTOName;
222       const BinaryFunction &Function = BFI.second;
223       const double Score = getNormalizedScore(Function, RI2);
224       LargestBin2.insert(std::make_pair<>(Score, &Function));
225       for (const StringRef &Name : Function.getNames()) {
226         if (std::optional<StringRef> OptionalLTOName = getLTOCommonName(Name))
227           LTOName = *OptionalLTOName;
228       }
229       if (opts::IgnoreLTOSuffix && !LTOName.empty()) {
230         if (!LTONameLookup2.count(LTOName))
231           LTONameLookup2[LTOName] = &Function;
232         LTOMap2[&Function] = LTONameLookup2[LTOName];
233       }
234     }
235   }
236 
237   /// Match functions in binary 2 with functions in binary 1
238   void matchFunctions() {
239     outs() << "BOLT-DIFF: Mapping functions in Binary2 to Binary1\n";
240     uint64_t BothHaveProfile = 0ull;
241     std::set<const BinaryFunction *> Bin1ProfiledMapped;
242 
243     for (const auto &BFI2 : RI2.BC->getBinaryFunctions()) {
244       const BinaryFunction &Function2 = BFI2.second;
245       StringRef LTOName;
246       bool Match = false;
247       for (const StringRef &Name : Function2.getNames()) {
248         auto Iter = NameLookup.find(Name);
249         if (std::optional<StringRef> OptionalLTOName = getLTOCommonName(Name))
250           LTOName = *OptionalLTOName;
251         if (Iter == NameLookup.end())
252           continue;
253         FuncMap.insert(std::make_pair<>(&Function2, Iter->second));
254         Bin1MappedFuncs.insert(Iter->second);
255         Bin2MappedFuncs.insert(&Function2);
256         if (Function2.hasValidProfile() && Iter->second->hasValidProfile()) {
257           ++BothHaveProfile;
258           Bin1ProfiledMapped.insert(Iter->second);
259         }
260         Match = true;
261         break;
262       }
263       if (Match || !Function2.hasCFG())
264         continue;
265       auto Iter = HashLookup.find(Function2.computeHash(/*UseDFS*/ true));
266       if (Iter != HashLookup.end()) {
267         FuncMap.insert(std::make_pair<>(&Function2, Iter->second));
268         Bin1MappedFuncs.insert(Iter->second);
269         Bin2MappedFuncs.insert(&Function2);
270         if (Function2.hasValidProfile() && Iter->second->hasValidProfile()) {
271           ++BothHaveProfile;
272           Bin1ProfiledMapped.insert(Iter->second);
273         }
274         continue;
275       }
276       if (LTOName.empty())
277         continue;
278       auto LTOIter = LTONameLookup1.find(LTOName);
279       if (LTOIter != LTONameLookup1.end()) {
280         FuncMap.insert(std::make_pair<>(&Function2, LTOIter->second));
281         Bin1MappedFuncs.insert(LTOIter->second);
282         Bin2MappedFuncs.insert(&Function2);
283         if (Function2.hasValidProfile() && LTOIter->second->hasValidProfile()) {
284           ++BothHaveProfile;
285           Bin1ProfiledMapped.insert(LTOIter->second);
286         }
287       }
288     }
289     PrintProgramStats PPS(opts::NeverPrint);
290     outs() << "* BOLT-DIFF: Starting print program stats pass for binary 1\n";
291     PPS.runOnFunctions(*RI1.BC);
292     outs() << "* BOLT-DIFF: Starting print program stats pass for binary 2\n";
293     PPS.runOnFunctions(*RI2.BC);
294     outs() << "=====\n";
295     outs() << "Inputs share " << BothHaveProfile
296            << " functions with valid profile.\n";
297     if (opts::PrintProfiledUnmapped) {
298       outs() << "\nFunctions in profile 1 that are missing in the profile 2:\n";
299       std::vector<const BinaryFunction *> Unmapped;
300       for (const auto &BFI : RI1.BC->getBinaryFunctions()) {
301         const BinaryFunction &Function = BFI.second;
302         if (!Function.hasValidProfile() || Bin1ProfiledMapped.count(&Function))
303           continue;
304         Unmapped.emplace_back(&Function);
305       }
306       llvm::sort(Unmapped,
307                  [&](const BinaryFunction *A, const BinaryFunction *B) {
308                    return A->getFunctionScore() > B->getFunctionScore();
309                  });
310       for (const BinaryFunction *Function : Unmapped) {
311         outs() << Function->getPrintName() << " : ";
312         outs() << Function->getFunctionScore() << "\n";
313       }
314       outs() << "=====\n";
315     }
316   }
317 
318   /// Check if opcodes in BB1 match those in BB2
319   bool compareBBs(const BinaryBasicBlock &BB1,
320                   const BinaryBasicBlock &BB2) const {
321     auto Iter1 = BB1.begin();
322     auto Iter2 = BB2.begin();
323     if ((Iter1 == BB1.end() && Iter2 != BB2.end()) ||
324         (Iter1 != BB1.end() && Iter2 == BB2.end()))
325       return false;
326 
327     while (Iter1 != BB1.end()) {
328       if (Iter2 == BB2.end() || Iter1->getOpcode() != Iter2->getOpcode())
329         return false;
330 
331       ++Iter1;
332       ++Iter2;
333     }
334 
335     if (Iter2 != BB2.end())
336       return false;
337     return true;
338   }
339 
340   /// For a function in binary 2 that matched one in binary 1, now match each
341   /// individual basic block in it to its corresponding blocks in binary 1.
342   /// Also match each edge in binary 2 to the corresponding ones in binary 1.
343   void matchBasicBlocks() {
344     for (const auto &MapEntry : FuncMap) {
345       const BinaryFunction *const &Func1 = MapEntry.second;
346       const BinaryFunction *const &Func2 = MapEntry.first;
347 
348       auto Iter1 = Func1->getLayout().block_begin();
349       auto Iter2 = Func2->getLayout().block_begin();
350 
351       bool Match = true;
352       std::map<const BinaryBasicBlock *, const BinaryBasicBlock *> Map;
353       std::map<double, std::pair<EdgeTy, EdgeTy>> EMap;
354       while (Iter1 != Func1->getLayout().block_end()) {
355         if (Iter2 == Func2->getLayout().block_end()) {
356           Match = false;
357           break;
358         }
359         if (!compareBBs(**Iter1, **Iter2)) {
360           Match = false;
361           break;
362         }
363         Map.insert(std::make_pair<>(*Iter2, *Iter1));
364 
365         auto SuccIter1 = (*Iter1)->succ_begin();
366         auto SuccIter2 = (*Iter2)->succ_begin();
367         auto BIIter1 = (*Iter1)->branch_info_begin();
368         auto BIIter2 = (*Iter2)->branch_info_begin();
369         while (SuccIter1 != (*Iter1)->succ_end()) {
370           if (SuccIter2 == (*Iter2)->succ_end()) {
371             Match = false;
372             break;
373           }
374           const double ScoreEdge1 = getNormalizedScore(BIIter1, RI1);
375           const double ScoreEdge2 = getNormalizedScore(BIIter2, RI2);
376           EMap.insert(std::make_pair<>(
377               std::abs(ScoreEdge2 - ScoreEdge1),
378               std::make_pair<>(
379                   std::make_tuple<>(*Iter2, *SuccIter2, ScoreEdge2),
380                   std::make_tuple<>(*Iter1, *SuccIter1, ScoreEdge1))));
381 
382           ++SuccIter1;
383           ++SuccIter2;
384           ++BIIter1;
385           ++BIIter2;
386         }
387         if (SuccIter2 != (*Iter2)->succ_end())
388           Match = false;
389         if (!Match)
390           break;
391 
392         BBToFuncMap[*Iter1] = Func1;
393         BBToFuncMap[*Iter2] = Func2;
394         ++Iter1;
395         ++Iter2;
396       }
397       if (!Match || Iter2 != Func2->getLayout().block_end())
398         continue;
399 
400       BBMap.insert(Map.begin(), Map.end());
401       EdgeMap.insert(EMap.begin(), EMap.end());
402     }
403   }
404 
405   /// Print the largest differences in basic block performance from binary 1
406   /// to binary 2
407   void reportHottestBBDiffs() {
408     std::map<double, const BinaryBasicBlock *> LargestDiffs;
409     for (const auto &MapEntry : BBMap) {
410       const BinaryBasicBlock *BB2 = MapEntry.first;
411       const BinaryBasicBlock *BB1 = MapEntry.second;
412       LargestDiffs.insert(
413           std::make_pair<>(std::abs(getNormalizedScore(*BB2, RI2) -
414                                     getNormalizedScore(*BB1, RI1)),
415                            BB2));
416     }
417 
418     unsigned Printed = 0;
419     setTitleColor();
420     outs()
421         << "\nTop " << opts::DisplayCount
422         << " largest differences in basic block performance bin 2 -> bin 1:\n";
423     outs() << "=========================================================\n";
424     setRegularColor();
425     outs() << " * Functions with different contents do not appear here\n\n";
426     for (const BinaryBasicBlock *BB2 :
427          llvm::make_second_range(llvm::reverse(LargestDiffs))) {
428       const double Score2 = getNormalizedScore(*BB2, RI2);
429       const double Score1 = getNormalizedScore(*BBMap[BB2], RI1);
430       outs() << "BB " << BB2->getName() << " from "
431              << BBToFuncMap[BB2]->getDemangledName()
432              << "\n\tScore bin1 = " << format("%.4f", Score1 * 100.0)
433              << "%\n\tScore bin2 = " << format("%.4f", Score2 * 100.0);
434       outs() << "%\t(Difference: ";
435       printColoredPercentage((Score2 - Score1) * 100.0);
436       outs() << ")\n";
437       if (opts::PrintDiffBBs) {
438         setLightColor();
439         BB2->dump();
440         setRegularColor();
441       }
442       if (Printed++ == opts::DisplayCount)
443         break;
444     }
445   }
446 
447   /// Print the largest differences in edge counts from one binary to another
448   void reportHottestEdgeDiffs() {
449     unsigned Printed = 0;
450     setTitleColor();
451     outs() << "\nTop " << opts::DisplayCount
452            << " largest differences in edge hotness bin 2 -> bin 1:\n";
453     outs() << "=========================================================\n";
454     setRegularColor();
455     outs() << " * Functions with different contents do not appear here\n";
456     for (std::pair<EdgeTy, EdgeTy> &EI :
457          llvm::make_second_range(llvm::reverse(EdgeMap))) {
458       EdgeTy &Edge2 = EI.first;
459       EdgeTy &Edge1 = EI.second;
460       const double Score2 = std::get<2>(Edge2);
461       const double Score1 = std::get<2>(Edge1);
462       outs() << "Edge (" << std::get<0>(Edge2)->getName() << " -> "
463              << std::get<1>(Edge2)->getName() << ") in "
464              << BBToFuncMap[std::get<0>(Edge2)]->getDemangledName()
465              << "\n\tScore bin1 = " << format("%.4f", Score1 * 100.0)
466              << "%\n\tScore bin2 = " << format("%.4f", Score2 * 100.0);
467       outs() << "%\t(Difference: ";
468       printColoredPercentage((Score2 - Score1) * 100.0);
469       outs() << ")\n";
470       if (opts::PrintDiffBBs) {
471         setLightColor();
472         std::get<0>(Edge2)->dump();
473         std::get<1>(Edge2)->dump();
474         setRegularColor();
475       }
476       if (Printed++ == opts::DisplayCount)
477         break;
478     }
479   }
480 
481   /// For LTO functions sharing the same prefix (for example, func1.lto_priv.1
482   /// and func1.lto_priv.2 share the func1.lto_priv prefix), compute aggregated
483   /// scores for them. This is used to avoid reporting all LTO functions as
484   /// having a large difference in performance because hotness shifted from
485   /// LTO variant 1 to variant 2, even though they represent the same function.
486   void computeAggregatedLTOScore() {
487     for (const auto &BFI : RI1.BC->getBinaryFunctions()) {
488       const BinaryFunction &Function = BFI.second;
489       double Score = getNormalizedScore(Function, RI1);
490       auto Iter = LTOMap1.find(&Function);
491       if (Iter == LTOMap1.end())
492         continue;
493       LTOAggregatedScore1[Iter->second] += Score;
494     }
495 
496     double UnmappedScore = 0;
497     for (const auto &BFI : RI2.BC->getBinaryFunctions()) {
498       const BinaryFunction &Function = BFI.second;
499       bool Matched = FuncMap.find(&Function) != FuncMap.end();
500       double Score = getNormalizedScore(Function, RI2);
501       auto Iter = LTOMap2.find(&Function);
502       if (Iter == LTOMap2.end()) {
503         if (!Matched)
504           UnmappedScore += Score;
505         continue;
506       }
507       LTOAggregatedScore2[Iter->second] += Score;
508       if (FuncMap.find(Iter->second) == FuncMap.end())
509         UnmappedScore += Score;
510     }
511     int64_t Unmapped =
512         RI2.BC->getBinaryFunctions().size() - Bin2MappedFuncs.size();
513     outs() << "BOLT-DIFF: " << Unmapped
514            << " functions in Binary2 have no correspondence to any other "
515               "function in Binary1.\n";
516 
517     // Print the hotness score of functions in binary 2 that were not matched
518     // to any function in binary 1
519     outs() << "BOLT-DIFF: These unmapped functions in Binary2 represent "
520            << format("%.2f", UnmappedScore * 100.0) << "% of execution.\n";
521   }
522 
523   /// Print the largest hotness differences from binary 2 to binary 1
524   void reportHottestFuncDiffs() {
525     std::multimap<double, decltype(FuncMap)::value_type> LargestDiffs;
526     for (const auto &MapEntry : FuncMap) {
527       const BinaryFunction *const &Func1 = MapEntry.second;
528       const BinaryFunction *const &Func2 = MapEntry.first;
529       double Score1 = getNormalizedScore(*Func1, RI1);
530       auto Iter1 = LTOMap1.find(Func1);
531       if (Iter1 != LTOMap1.end())
532         Score1 = LTOAggregatedScore1[Iter1->second];
533       double Score2 = getNormalizedScore(*Func2, RI2);
534       auto Iter2 = LTOMap2.find(Func2);
535       if (Iter2 != LTOMap2.end())
536         Score2 = LTOAggregatedScore2[Iter2->second];
537       if (Score1 == 0.0 || Score2 == 0.0)
538         continue;
539       LargestDiffs.insert(
540           std::make_pair<>(std::abs(Score1 - Score2), MapEntry));
541       ScoreMap[Func2] = std::make_pair<>(Score1, Score2);
542     }
543 
544     unsigned Printed = 0;
545     setTitleColor();
546     outs() << "\nTop " << opts::DisplayCount
547            << " largest differences in performance bin 2 -> bin 1:\n";
548     outs() << "=========================================================\n";
549     setRegularColor();
550     for (decltype(this->FuncMap)::value_type &MapEntry :
551          llvm::make_second_range(llvm::reverse(LargestDiffs))) {
552       if (opts::IgnoreUnchanged &&
553           MapEntry.second->computeHash(/*UseDFS=*/true) ==
554               MapEntry.first->computeHash(/*UseDFS=*/true))
555         continue;
556       const std::pair<double, double> &Scores = ScoreMap[MapEntry.first];
557       outs() << "Function " << MapEntry.first->getDemangledName();
558       if (MapEntry.first->getDemangledName() !=
559           MapEntry.second->getDemangledName())
560         outs() << "\nmatched  " << MapEntry.second->getDemangledName();
561       outs() << "\n\tScore bin1 = " << format("%.2f", Scores.first * 100.0)
562              << "%\n\tScore bin2 = " << format("%.2f", Scores.second * 100.0)
563              << "%\t(Difference: ";
564       printColoredPercentage((Scores.second - Scores.first) * 100.0);
565       outs() << ")";
566       if (MapEntry.second->computeHash(/*UseDFS=*/true) !=
567           MapEntry.first->computeHash(/*UseDFS=*/true)) {
568         outs() << "\t[Functions have different contents]";
569         if (opts::PrintDiffCFG) {
570           outs() << "\n *** CFG for function in binary 1:\n";
571           setLightColor();
572           MapEntry.second->dump();
573           setRegularColor();
574           outs() << "\n *** CFG for function in binary 2:\n";
575           setLightColor();
576           MapEntry.first->dump();
577           setRegularColor();
578         }
579       }
580       outs() << "\n";
581       if (Printed++ == opts::DisplayCount)
582         break;
583     }
584   }
585 
586   /// Print hottest functions from each binary
587   void reportHottestFuncs() {
588     unsigned Printed = 0;
589     setTitleColor();
590     outs() << "\nTop " << opts::DisplayCount
591            << " hottest functions in binary 2:\n";
592     outs() << "=====================================\n";
593     setRegularColor();
594     for (std::pair<const double, const BinaryFunction *> &MapEntry :
595          llvm::reverse(LargestBin2)) {
596       outs() << "Function " << MapEntry.second->getDemangledName() << "\n";
597       auto Iter = ScoreMap.find(MapEntry.second);
598       if (Iter != ScoreMap.end())
599         outs() << "\tScore bin1 = "
600                << format("%.2f", Iter->second.first * 100.0) << "%\n";
601       outs() << "\tScore bin2 = " << format("%.2f", MapEntry.first * 100.0)
602              << "%\n";
603       if (Printed++ == opts::DisplayCount)
604         break;
605     }
606 
607     Printed = 0;
608     setTitleColor();
609     outs() << "\nTop " << opts::DisplayCount
610            << " hottest functions in binary 1:\n";
611     outs() << "=====================================\n";
612     setRegularColor();
613     for (const std::pair<const double, const BinaryFunction *> &MapEntry :
614          llvm::reverse(LargestBin1)) {
615       outs() << "Function " << MapEntry.second->getDemangledName()
616              << "\n\tScore bin1 = " << format("%.2f", MapEntry.first * 100.0)
617              << "%\n";
618       if (Printed++ == opts::DisplayCount)
619         break;
620     }
621   }
622 
623   /// Print functions in binary 2 that did not match anything in binary 1.
624   /// Unfortunately, in an LTO build, even a small change can lead to several
625   /// LTO variants being unmapped, corresponding to local functions that never
626   /// appear in one of the binaries because they were previously inlined.
627   void reportUnmapped() {
628     outs() << "List of functions from binary 2 that were not matched with any "
629            << "function in binary 1:\n";
630     for (const auto &BFI2 : RI2.BC->getBinaryFunctions()) {
631       const BinaryFunction &Function2 = BFI2.second;
632       if (Bin2MappedFuncs.count(&Function2))
633         continue;
634       outs() << Function2.getPrintName() << "\n";
635     }
636   }
637 
638 public:
639   /// Main entry point: coordinate all tasks necessary to compare two binaries
640   void compareAndReport() {
641     buildLookupMaps();
642     matchFunctions();
643     if (opts::IgnoreLTOSuffix)
644       computeAggregatedLTOScore();
645     matchBasicBlocks();
646     reportHottestFuncDiffs();
647     reportHottestBBDiffs();
648     reportHottestEdgeDiffs();
649     reportHottestFuncs();
650     if (!opts::PrintUnmapped)
651       return;
652     reportUnmapped();
653   }
654 
655   RewriteInstanceDiff(RewriteInstance &RI1, RewriteInstance &RI2)
656       : RI1(RI1), RI2(RI2) {
657     compareAndReport();
658   }
659 
660 };
661 
662 } // end nampespace bolt
663 } // end namespace llvm
664 
665 void RewriteInstance::compare(RewriteInstance &RI2) {
666   outs() << "BOLT-DIFF: ======== Binary1 vs. Binary2 ========\n";
667   outs() << "Trace for binary 1 has " << this->getTotalScore()
668          << " instructions executed.\n";
669   outs() << "Trace for binary 2 has " << RI2.getTotalScore()
670          << " instructions executed.\n";
671   if (opts::NormalizeByBin1) {
672     double Diff2to1 =
673         static_cast<double>(RI2.getTotalScore() - this->getTotalScore()) /
674         this->getTotalScore();
675     outs() << "Binary2 change in score with respect to Binary1: ";
676     printColoredPercentage(Diff2to1 * 100.0);
677     outs() << "\n";
678   }
679 
680   if (!this->getTotalScore() || !RI2.getTotalScore()) {
681     outs() << "BOLT-DIFF: Both binaries must have recorded activity in known "
682               "functions.\n";
683     return;
684   }
685 
686   // Pre-pass ICF
687   if (opts::ICF) {
688     IdenticalCodeFolding ICF(opts::NeverPrint);
689     outs() << "BOLT-DIFF: Starting ICF pass for binary 1";
690     ICF.runOnFunctions(*BC);
691     outs() << "BOLT-DIFF: Starting ICF pass for binary 2";
692     ICF.runOnFunctions(*RI2.BC);
693   }
694 
695   RewriteInstanceDiff RID(*this, RI2);
696 }
697