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