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