brintos

brintos / llvm-project-archived public Read only

0
0
Text · 26.4 KiB · 35f6710 Raw
712 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// RewriteInstance methods related to comparing one instance to another, used10// 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 "bolt/Utils/Utils.h"18#include "llvm/ADT/STLExtras.h"19#include "llvm/Support/CommandLine.h"20 21#undef  DEBUG_TYPE22#define DEBUG_TYPE "boltdiff"23 24using namespace llvm;25using namespace object;26using namespace bolt;27 28namespace opts {29extern cl::OptionCategory BoltDiffCategory;30extern cl::opt<bool> NeverPrint;31extern cl::opt<bolt::IdenticalCodeFolding::ICFLevel, false,32               llvm::bolt::DeprecatedICFNumericOptionParser>33    ICF;34 35static 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));39 40static 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));45 46static 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));51 52static 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));57 58static cl::opt<bool>59    PrintDiffBBs("print-diff-bbs",60                 cl::desc("print the basic blocks showed in top differences"),61                 cl::cat(BoltDiffCategory));62 63static 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));68 69static 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));74 75static 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));80 81static 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));87 88static cl::opt<bool>89    SkipNonSimple("skip-non-simple",90                  cl::desc("skip non-simple functions in reporting"),91                  cl::ReallyHidden, cl::cat(BoltDiffCategory));92 93} // end namespace opts94 95namespace llvm {96namespace bolt {97 98namespace {99 100/// Helper used to print colored numbers101void 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();111}112 113void setLightColor() {114  if (opts::PrintDiffBBs && outs().has_colors())115    outs().changeColor(raw_ostream::CYAN);116}117 118void setTitleColor() {119  if (outs().has_colors())120    outs().changeColor(raw_ostream::WHITE, /*Bold=*/true);121}122 123void setRegularColor() {124  if (outs().has_colors())125    outs().resetColor();126}127 128} // end anonymous namespace129 130/// Perform the comparison between two binaries with profiling information131class RewriteInstanceDiff {132  typedef std::tuple<const BinaryBasicBlock *, const BinaryBasicBlock *, double>133      EdgeTy;134 135  RewriteInstance &RI1;136  RewriteInstance &RI2;137 138  // The map of functions keyed by functions in binary 2, providing its139  // corresponding function in binary 1140  std::map<const BinaryFunction *, const BinaryFunction *> FuncMap;141 142  // The map of basic blocks correspondence, analogue to FuncMap for BBs,143  // sorted by score difference144  std::map<const BinaryBasicBlock *, const BinaryBasicBlock *> BBMap;145 146  // The map of edge correspondence147  std::map<double, std::pair<EdgeTy, EdgeTy>> EdgeMap;148 149  // Maps all known basic blocks back to their parent function150  std::map<const BinaryBasicBlock *, const BinaryFunction *> BBToFuncMap;151 152  // Accounting which functions were matched153  std::set<const BinaryFunction *> Bin1MappedFuncs;154  std::set<const BinaryFunction *> Bin2MappedFuncs;155 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 functions158  StringMap<const BinaryFunction *> NameLookup;159  DenseMap<size_t, const BinaryFunction *> HashLookup;160  StringMap<const BinaryFunction *> LTONameLookup1;161  StringMap<const BinaryFunction *> LTONameLookup2;162 163  // Score maps used to order and find hottest functions164  std::multimap<double, const BinaryFunction *> LargestBin1;165  std::multimap<double, const BinaryFunction *> LargestBin2;166 167  // Map multiple functions in the same LTO bucket to a single parent function168  // representing all functions sharing the same prefix169  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;173 174  // Map scores in bin2 and 1 keyed by a binary 2 function - post-matching175  DenseMap<const BinaryFunction *, std::pair<double, double>> ScoreMap;176 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();184  }185 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();193  }194 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();202  }203 204  /// Initialize data structures used for function lookup in binary 1, used205  /// later when matching functions in binary 2 to corresponding functions206  /// in binary 1207  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;217      }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];224      }225    }226 227    // Compute LTONameLookup2 and LargestBin2228    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;236      }237      if (opts::IgnoreLTOSuffix && !LTOName.empty()) {238        if (!LTONameLookup2.count(LTOName))239          LTONameLookup2[LTOName] = &Function;240        LTOMap2[&Function] = LTONameLookup2[LTOName];241      }242    }243  }244 245  /// Match functions in binary 2 with functions in binary 1246  void matchFunctions() {247    outs() << "BOLT-DIFF: Mapping functions in Binary2 to Binary1\n";248    uint64_t BothHaveProfile = 0ull;249    std::set<const BinaryFunction *> Bin1ProfiledMapped;250 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);267        }268        Match = true;269        break;270      }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);281        }282        continue;283      }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);294        }295      }296    }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 " << BothHaveProfile304           << " 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);313      }314      llvm::sort(Unmapped,315                 [&](const BinaryFunction *A, const BinaryFunction *B) {316                   return A->getFunctionScore() > B->getFunctionScore();317                 });318      for (const BinaryFunction *Function : Unmapped) {319        outs() << Function->getPrintName() << " : ";320        outs() << Function->getFunctionScore() << "\n";321      }322      outs() << "=====\n";323    }324  }325 326  /// Check if opcodes in BB1 match those in BB2327  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;334 335    while (Iter1 != BB1.end()) {336      if (Iter2 == BB2.end() || Iter1->getOpcode() != Iter2->getOpcode())337        return false;338 339      ++Iter1;340      ++Iter2;341    }342 343    if (Iter2 != BB2.end())344      return false;345    return true;346  }347 348  /// For a function in binary 2 that matched one in binary 1, now match each349  /// 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;355 356      auto Iter1 = Func1->getLayout().block_begin();357      auto Iter2 = Func2->getLayout().block_begin();358 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;366        }367        if (!compareBBs(**Iter1, **Iter2)) {368          Match = false;369          break;370        }371        Map.insert(std::make_pair<>(*Iter2, *Iter1));372 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;381          }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))));389 390          ++SuccIter1;391          ++SuccIter2;392          ++BIIter1;393          ++BIIter2;394        }395        if (SuccIter2 != (*Iter2)->succ_end())396          Match = false;397        if (!Match)398          break;399 400        BBToFuncMap[*Iter1] = Func1;401        BBToFuncMap[*Iter2] = Func2;402        ++Iter1;403        ++Iter2;404      }405      if (!Match || Iter2 != Func2->getLayout().block_end())406        continue;407 408      BBMap.insert(Map.begin(), Map.end());409      EdgeMap.insert(EMap.begin(), EMap.end());410    }411  }412 413  /// Print the largest differences in basic block performance from binary 1414  /// to binary 2415  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));424    }425 426    unsigned Printed = 0;427    setTitleColor();428    outs()429        << "\nTop " << opts::DisplayCount430        << " 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();451      }452      if (Printed++ == opts::DisplayCount)453        break;454    }455  }456 457  /// Print the largest differences in edge counts from one binary to another458  void reportHottestEdgeDiffs() {459    unsigned Printed = 0;460    setTitleColor();461    outs() << "\nTop " << opts::DisplayCount462           << " 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();488      }489      if (Printed++ == opts::DisplayCount)490        break;491    }492  }493 494  /// For LTO functions sharing the same prefix (for example, func1.lto_priv.1495  /// and func1.lto_priv.2 share the func1.lto_priv prefix), compute aggregated496  /// scores for them. This is used to avoid reporting all LTO functions as497  /// having a large difference in performance because hotness shifted from498  /// 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;507    }508 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;519      }520      LTOAggregatedScore2[Iter->second] += Score;521      if (FuncMap.find(Iter->second) == FuncMap.end())522        UnmappedScore += Score;523    }524    int64_t Unmapped =525        RI2.BC->getBinaryFunctions().size() - Bin2MappedFuncs.size();526    outs() << "BOLT-DIFF: " << Unmapped527           << " functions in Binary2 have no correspondence to any other "528              "function in Binary1.\n";529 530    // Print the hotness score of functions in binary 2 that were not matched531    // to any function in binary 1532    outs() << "BOLT-DIFF: These unmapped functions in Binary2 represent "533           << format("%.2f", UnmappedScore * 100.0) << "% of execution.\n";534  }535 536  /// Print the largest hotness differences from binary 2 to binary 1537  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);557    }558 559    unsigned Printed = 0;560    setTitleColor();561    outs() << "\nTop " << opts::DisplayCount562           << " 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();593        }594      }595      outs() << "\n";596      if (Printed++ == opts::DisplayCount)597        break;598    }599  }600 601  /// Print hottest functions from each binary602  void reportHottestFuncs() {603    unsigned Printed = 0;604    setTitleColor();605    outs() << "\nTop " << opts::DisplayCount606           << " 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;620    }621 622    Printed = 0;623    setTitleColor();624    outs() << "\nTop " << opts::DisplayCount625           << " 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;635    }636  }637 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 several640  /// LTO variants being unmapped, corresponding to local functions that never641  /// 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";650    }651  }652 653public:654  /// Main entry point: coordinate all tasks necessary to compare two binaries655  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();668  }669 670  RewriteInstanceDiff(RewriteInstance &RI1, RewriteInstance &RI2)671      : RI1(RI1), RI2(RI2) {672    compareAndReport();673  }674 675};676 677} // end namespace bolt678} // end namespace llvm679 680void 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";693  }694 695  if (!this->getTotalScore() || !RI2.getTotalScore()) {696    outs() << "BOLT-DIFF: Both binaries must have recorded activity in known "697              "functions.\n";698    return;699  }700 701  // Pre-pass ICF702  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));708  }709 710  RewriteInstanceDiff RID(*this, RI2);711}712