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