brintos

brintos / llvm-project-archived public Read only

0
0
Text · 16.3 KiB · e4b4fc2 Raw
496 lines · cpp
1//===------------------ llvm-opt-report/OptReport.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/// \file10/// This file implements a tool that can parse the YAML optimization11/// records and generate an optimization summary annotated source listing12/// report.13///14//===----------------------------------------------------------------------===//15 16#include "llvm-c/Remarks.h"17#include "llvm/ADT/StringExtras.h"18#include "llvm/Demangle/Demangle.h"19#include "llvm/Remarks/Remark.h"20#include "llvm/Remarks/RemarkFormat.h"21#include "llvm/Remarks/RemarkParser.h"22#include "llvm/Support/CommandLine.h"23#include "llvm/Support/Error.h"24#include "llvm/Support/ErrorOr.h"25#include "llvm/Support/FileSystem.h"26#include "llvm/Support/Format.h"27#include "llvm/Support/InitLLVM.h"28#include "llvm/Support/LineIterator.h"29#include "llvm/Support/MemoryBuffer.h"30#include "llvm/Support/Path.h"31#include "llvm/Support/Program.h"32#include "llvm/Support/TypeSize.h"33#include "llvm/Support/WithColor.h"34#include "llvm/Support/raw_ostream.h"35#include <cstdlib>36#include <map>37#include <optional>38#include <set>39 40using namespace llvm;41 42// Mark all our options with this category, everything else (except for -version43// and -help) will be hidden.44static cl::OptionCategory45    OptReportCategory("llvm-opt-report options");46 47static cl::opt<std::string>48  InputFileName(cl::Positional, cl::desc("<input>"), cl::init("-"),49                cl::cat(OptReportCategory));50 51static cl::opt<std::string>52  OutputFileName("o", cl::desc("Output file"), cl::init("-"),53                 cl::cat(OptReportCategory));54 55static cl::opt<std::string>56  InputRelDir("r", cl::desc("Root for relative input paths"), cl::init(""),57              cl::cat(OptReportCategory));58 59static cl::opt<bool>60  Succinct("s", cl::desc("Don't include vectorization factors, etc."),61           cl::init(false), cl::cat(OptReportCategory));62 63static cl::opt<bool>64  NoDemangle("no-demangle", cl::desc("Don't demangle function names"),65             cl::init(false), cl::cat(OptReportCategory));66 67static cl::opt<std::string> ParserFormat("format",68                                         cl::desc("The format of the remarks."),69                                         cl::init("yaml"),70                                         cl::cat(OptReportCategory));71 72namespace {73// For each location in the source file, the common per-transformation state74// collected.75struct OptReportLocationItemInfo {76  bool Analyzed = false;77  bool Transformed = false;78 79  OptReportLocationItemInfo &operator |= (80    const OptReportLocationItemInfo &RHS) {81    Analyzed |= RHS.Analyzed;82    Transformed |= RHS.Transformed;83 84    return *this;85  }86 87  bool operator < (const OptReportLocationItemInfo &RHS) const {88    if (Analyzed < RHS.Analyzed)89      return true;90    else if (Analyzed > RHS.Analyzed)91      return false;92    else if (Transformed < RHS.Transformed)93      return true;94    return false;95  }96};97 98// The per-location information collected for producing an optimization report.99struct OptReportLocationInfo {100  OptReportLocationItemInfo Inlined;101  OptReportLocationItemInfo Unrolled;102  OptReportLocationItemInfo Vectorized;103 104  ElementCount VectorizationFactor = ElementCount::getFixed(1);105  int InterleaveCount = 1;106  int UnrollCount = 1;107 108  OptReportLocationInfo &operator |= (const OptReportLocationInfo &RHS) {109    Inlined |= RHS.Inlined;110    Unrolled |= RHS.Unrolled;111    Vectorized |= RHS.Vectorized;112 113    if (ElementCount::isKnownLT(VectorizationFactor, RHS.VectorizationFactor))114      VectorizationFactor = RHS.VectorizationFactor;115 116    InterleaveCount = std::max(InterleaveCount, RHS.InterleaveCount);117    UnrollCount = std::max(UnrollCount, RHS.UnrollCount);118 119    return *this;120  }121 122  bool operator < (const OptReportLocationInfo &RHS) const {123    if (Inlined < RHS.Inlined)124      return true;125    else if (RHS.Inlined < Inlined)126      return false;127    else if (Unrolled < RHS.Unrolled)128      return true;129    else if (RHS.Unrolled < Unrolled)130      return false;131    else if (Vectorized < RHS.Vectorized)132      return true;133    else if (RHS.Vectorized < Vectorized || Succinct)134      return false;135    else if (ElementCount::isKnownLT(VectorizationFactor,136                                     RHS.VectorizationFactor))137      return true;138    else if (ElementCount::isKnownGT(VectorizationFactor,139                                     RHS.VectorizationFactor))140      return false;141    else if (InterleaveCount < RHS.InterleaveCount)142      return true;143    else if (InterleaveCount > RHS.InterleaveCount)144      return false;145    else if (UnrollCount < RHS.UnrollCount)146      return true;147    return false;148  }149};150 151typedef std::map<std::string, std::map<int, std::map<std::string, std::map<int,152          OptReportLocationInfo>>>> LocationInfoTy;153} // anonymous namespace154 155static bool readLocationInfo(LocationInfoTy &LocationInfo) {156  ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =157      MemoryBuffer::getFile(InputFileName.c_str());158  if (std::error_code EC = Buf.getError()) {159    WithColor::error() << "Can't open file " << InputFileName << ": "160                       << EC.message() << "\n";161    return false;162  }163 164  Expected<remarks::Format> Format = remarks::parseFormat(ParserFormat);165  if (!Format) {166    handleAllErrors(Format.takeError(), [&](const ErrorInfoBase &PE) {167      PE.log(WithColor::error());168      errs() << '\n';169    });170    return false;171  }172 173  Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =174      remarks::createRemarkParserFromMeta(*Format, (*Buf)->getBuffer());175  if (!MaybeParser) {176    handleAllErrors(MaybeParser.takeError(), [&](const ErrorInfoBase &PE) {177      PE.log(WithColor::error());178      errs() << '\n';179    });180    return false;181  }182  remarks::RemarkParser &Parser = **MaybeParser;183 184  while (true) {185    Expected<std::unique_ptr<remarks::Remark>> MaybeRemark = Parser.next();186    if (!MaybeRemark) {187      Error E = MaybeRemark.takeError();188      if (E.isA<remarks::EndOfFileError>()) {189        // EOF.190        consumeError(std::move(E));191        break;192      }193      handleAllErrors(std::move(E), [&](const ErrorInfoBase &PE) {194        PE.log(WithColor::error());195        errs() << '\n';196      });197      return false;198    }199 200    const remarks::Remark &Remark = **MaybeRemark;201 202    bool Transformed = Remark.RemarkType == remarks::Type::Passed;203 204    ElementCount VectorizationFactor = ElementCount::getFixed(1);205    int InterleaveCount = 1;206    int UnrollCount = 1;207 208    for (const remarks::Argument &Arg : Remark.Args) {209      if (Arg.Key == "VectorizationFactor") {210        int MinValue = 1;211        bool IsScalable = false;212        if (Arg.Val.starts_with("vscale x ")) {213          Arg.Val.drop_front(9).getAsInteger(10, MinValue);214          IsScalable = true;215        } else {216          Arg.Val.getAsInteger(10, MinValue);217        }218        VectorizationFactor = ElementCount::get(MinValue, IsScalable);219      } else if (Arg.Key == "InterleaveCount") {220        Arg.Val.getAsInteger(10, InterleaveCount);221      } else if (Arg.Key == "UnrollCount") {222        Arg.Val.getAsInteger(10, UnrollCount);223      }224    }225 226    const std::optional<remarks::RemarkLocation> &Loc = Remark.Loc;227    if (!Loc)228      continue;229 230    StringRef File = Loc->SourceFilePath;231    unsigned Line = Loc->SourceLine;232    unsigned Column = Loc->SourceColumn;233 234    // We track information on both actual and potential transformations. This235    // way, if there are multiple possible things on a line that are, or could236    // have been transformed, we can indicate that explicitly in the output.237    auto UpdateLLII = [Transformed](OptReportLocationItemInfo &LLII) {238      LLII.Analyzed = true;239      if (Transformed)240        LLII.Transformed = true;241    };242 243    if (Remark.PassName == "inline") {244      auto &LI = LocationInfo[std::string(File)][Line]245                             [std::string(Remark.FunctionName)][Column];246      UpdateLLII(LI.Inlined);247    } else if (Remark.PassName == "loop-unroll") {248      auto &LI = LocationInfo[std::string(File)][Line]249                             [std::string(Remark.FunctionName)][Column];250      LI.UnrollCount = UnrollCount;251      UpdateLLII(LI.Unrolled);252    } else if (Remark.PassName == "loop-vectorize") {253      auto &LI = LocationInfo[std::string(File)][Line]254                             [std::string(Remark.FunctionName)][Column];255      LI.VectorizationFactor = VectorizationFactor;256      LI.InterleaveCount = InterleaveCount;257      UpdateLLII(LI.Vectorized);258    }259  }260 261  return true;262}263 264static bool writeReport(LocationInfoTy &LocationInfo) {265  std::error_code EC;266  llvm::raw_fd_ostream OS(OutputFileName, EC, llvm::sys::fs::OF_TextWithCRLF);267  if (EC) {268    WithColor::error() << "Can't open file " << OutputFileName << ": "269                       << EC.message() << "\n";270    return false;271  }272 273  bool FirstFile = true;274  for (auto &FI : LocationInfo) {275    SmallString<128> FileName(FI.first);276    if (!InputRelDir.empty())277      sys::path::make_absolute(InputRelDir, FileName);278 279    const auto &FileInfo = FI.second;280 281    ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =282        MemoryBuffer::getFile(FileName);283    if (std::error_code EC = Buf.getError()) {284      WithColor::error() << "Can't open file " << FileName << ": "285                         << EC.message() << "\n";286      return false;287    }288 289    if (FirstFile)290      FirstFile = false;291    else292      OS << "\n";293 294    OS << "< " << FileName << "\n";295 296    // Figure out how many characters we need for the vectorization factors297    // and similar.298    OptReportLocationInfo MaxLI;299    for (auto &FLI : FileInfo)300      for (auto &FI : FLI.second)301        for (auto &LI : FI.second)302          MaxLI |= LI.second;303 304    bool NothingInlined = !MaxLI.Inlined.Transformed;305    bool NothingUnrolled = !MaxLI.Unrolled.Transformed;306    bool NothingVectorized = !MaxLI.Vectorized.Transformed;307 308    unsigned VFDigits =309        llvm::utostr(MaxLI.VectorizationFactor.getKnownMinValue()).size();310    if (MaxLI.VectorizationFactor.isScalable())311      VFDigits += 2; // For "Nx..."312 313    unsigned ICDigits = llvm::utostr(MaxLI.InterleaveCount).size();314    unsigned UCDigits = llvm::utostr(MaxLI.UnrollCount).size();315 316    // Figure out how many characters we need for the line numbers.317    int64_t NumLines = 0;318    for (line_iterator LI(*Buf.get(), false); LI != line_iterator(); ++LI)319      ++NumLines;320 321    unsigned LNDigits = llvm::utostr(NumLines).size();322 323    for (line_iterator LI(*Buf.get(), false); LI != line_iterator(); ++LI) {324      int64_t L = LI.line_number();325      auto LII = FileInfo.find(L);326 327      auto PrintLine = [&](bool PrintFuncName,328                           const std::set<std::string> &FuncNameSet) {329        OptReportLocationInfo LLI;330 331        std::map<int, OptReportLocationInfo> ColsInfo;332        unsigned InlinedCols = 0, UnrolledCols = 0, VectorizedCols = 0;333 334        if (LII != FileInfo.end() && !FuncNameSet.empty()) {335          const auto &LineInfo = LII->second;336 337          for (auto &CI : LineInfo.find(*FuncNameSet.begin())->second) {338            int Col = CI.first;339            ColsInfo[Col] = CI.second;340            InlinedCols += CI.second.Inlined.Analyzed;341            UnrolledCols += CI.second.Unrolled.Analyzed;342            VectorizedCols += CI.second.Vectorized.Analyzed;343            LLI |= CI.second;344          }345        }346 347        if (PrintFuncName) {348          OS << "  > ";349 350          bool FirstFunc = true;351          for (const auto &FuncName : FuncNameSet) {352            if (FirstFunc)353              FirstFunc = false;354            else355              OS << ", ";356 357            bool Printed = false;358            if (!NoDemangle) {359              if (char *Demangled = itaniumDemangle(FuncName)) {360                OS << Demangled;361                Printed = true;362                std::free(Demangled);363              }364            }365 366            if (!Printed)367              OS << FuncName;368          }369 370          OS << ":\n";371        }372 373        // We try to keep the output as concise as possible. If only one thing on374        // a given line could have been inlined, vectorized, etc. then we can put375        // the marker on the source line itself. If there are multiple options376        // then we want to distinguish them by placing the marker for each377        // transformation on a separate line following the source line. When we378        // do this, we use a '^' character to point to the appropriate column in379        // the source line.380 381        std::string USpaces(Succinct ? 0 : UCDigits, ' ');382        std::string VSpaces(Succinct ? 0 : VFDigits + ICDigits + 1, ' ');383 384        auto UStr = [UCDigits](OptReportLocationInfo &LLI) {385          std::string R;386          raw_string_ostream RS(R);387 388          if (!Succinct) {389            RS << LLI.UnrollCount;390            RS << std::string(UCDigits - R.size(), ' ');391          }392 393          return R;394        };395 396        auto VStr = [VFDigits,397                     ICDigits](OptReportLocationInfo &LLI) -> std::string {398          std::string R;399          raw_string_ostream RS(R);400 401          if (!Succinct) {402            if (LLI.VectorizationFactor.isScalable())403              RS << "Nx";404            RS << LLI.VectorizationFactor.getKnownMinValue() << ","405               << LLI.InterleaveCount;406            RS << std::string(VFDigits + ICDigits + 1 - R.size(), ' ');407          }408 409          return R;410        };411 412        OS << llvm::format_decimal(L, LNDigits) << " ";413        OS << (LLI.Inlined.Transformed && InlinedCols < 2 ? "I" :414                (NothingInlined ? "" : " "));415        OS << (LLI.Unrolled.Transformed && UnrolledCols < 2 ?416                "U" + UStr(LLI) : (NothingUnrolled ? "" : " " + USpaces));417        OS << (LLI.Vectorized.Transformed && VectorizedCols < 2 ?418                "V" + VStr(LLI) : (NothingVectorized ? "" : " " + VSpaces));419 420        OS << " | " << *LI << "\n";421 422        for (auto &J : ColsInfo) {423          if ((J.second.Inlined.Transformed && InlinedCols > 1) ||424              (J.second.Unrolled.Transformed && UnrolledCols > 1) ||425              (J.second.Vectorized.Transformed && VectorizedCols > 1)) {426            OS << std::string(LNDigits + 1, ' ');427            OS << (J.second.Inlined.Transformed &&428                   InlinedCols > 1 ? "I" : (NothingInlined ? "" : " "));429            OS << (J.second.Unrolled.Transformed &&430                   UnrolledCols > 1 ? "U" + UStr(J.second) :431                     (NothingUnrolled ? "" : " " + USpaces));432            OS << (J.second.Vectorized.Transformed &&433                   VectorizedCols > 1 ? "V" + VStr(J.second) :434                     (NothingVectorized ? "" : " " + VSpaces));435 436            OS << " | " << std::string(J.first - 1, ' ') << "^\n";437          }438        }439      };440 441      // We need to figure out if the optimizations for this line were the same442      // in each function context. If not, then we want to group the similar443      // function contexts together and display each group separately. If444      // they're all the same, then we only display the line once without any445      // additional markings.446      std::map<std::map<int, OptReportLocationInfo>,447               std::set<std::string>> UniqueLIs;448 449      OptReportLocationInfo AllLI;450      if (LII != FileInfo.end()) {451        const auto &FuncLineInfo = LII->second;452        for (const auto &FLII : FuncLineInfo) {453          UniqueLIs[FLII.second].insert(FLII.first);454 455          for (const auto &OI : FLII.second)456            AllLI |= OI.second;457        }458      }459 460      bool NothingHappened = !AllLI.Inlined.Transformed &&461                             !AllLI.Unrolled.Transformed &&462                             !AllLI.Vectorized.Transformed;463      if (UniqueLIs.size() > 1 && !NothingHappened) {464        OS << " [[\n";465        for (const auto &FSLI : UniqueLIs)466          PrintLine(true, FSLI.second);467        OS << " ]]\n";468      } else if (UniqueLIs.size() == 1) {469        PrintLine(false, UniqueLIs.begin()->second);470      } else {471        PrintLine(false, std::set<std::string>());472      }473    }474  }475 476  return true;477}478 479int main(int argc, const char **argv) {480  InitLLVM X(argc, argv);481 482  cl::HideUnrelatedOptions(OptReportCategory);483  cl::ParseCommandLineOptions(484      argc, argv,485      "A tool to generate an optimization report from YAML optimization"486      " record files.\n");487 488  LocationInfoTy LocationInfo;489  if (!readLocationInfo(LocationInfo))490    return 1;491  if (!writeReport(LocationInfo))492    return 1;493 494  return 0;495}496