diff options
Diffstat (limited to 'llvm/tools/llvm-opt-report/OptReport.cpp')
-rw-r--r-- | llvm/tools/llvm-opt-report/OptReport.cpp | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/llvm/tools/llvm-opt-report/OptReport.cpp b/llvm/tools/llvm-opt-report/OptReport.cpp new file mode 100644 index 00000000000..0c3acb5e5f4 --- /dev/null +++ b/llvm/tools/llvm-opt-report/OptReport.cpp @@ -0,0 +1,401 @@ +//===------------------ llvm-opt-report/OptReport.cpp ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file implements a tool that can parse the YAML optimization +/// records and generate an optimization summary annotated source listing +/// report. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/YAMLTraits.h" + +using namespace llvm; +using namespace llvm::yaml; + +static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden); + +// Mark all our options with this category, everything else (except for -version +// and -help) will be hidden. +static cl::OptionCategory + OptReportCategory("llvm-opt-report options"); + +static cl::opt<std::string> + InputFileName(cl::Positional, cl::desc("<input>"), cl::init("-"), + cl::cat(OptReportCategory)); + +static cl::opt<std::string> + OutputFileName("o", cl::desc("Output file"), cl::init("-"), + cl::cat(OptReportCategory)); + +static cl::opt<std::string> + InputRelDir("r", cl::desc("Root for relative input paths"), cl::init(""), + cl::cat(OptReportCategory)); + +static cl::opt<bool> + Succinct("s", cl::desc("Don't include vectorization factors, etc."), + cl::init(false), cl::cat(OptReportCategory)); + +namespace { +// For each location in the source file, the common per-transformation state +// collected. +struct OptReportLocationItemInfo { + bool Analyzed = false; + bool Transformed = false; + + OptReportLocationItemInfo &operator |= ( + const OptReportLocationItemInfo &RHS) { + Analyzed |= RHS.Analyzed; + Transformed |= RHS.Transformed; + + return *this; + } +}; + +// The per-location information collected for producing an optimization report. +struct OptReportLocationInfo { + OptReportLocationItemInfo Inlined; + OptReportLocationItemInfo Unrolled; + OptReportLocationItemInfo Vectorized; + + int VectorizationFactor = 1; + int InterleaveCount = 1; + int UnrollCount = 1; + + OptReportLocationInfo &operator |= (const OptReportLocationInfo &RHS) { + Inlined |= RHS.Inlined; + Unrolled |= RHS.Unrolled; + Vectorized |= RHS.Vectorized; + + VectorizationFactor = + std::max(VectorizationFactor, RHS.VectorizationFactor); + InterleaveCount = std::max(InterleaveCount, RHS.InterleaveCount); + UnrollCount = std::max(UnrollCount, RHS.UnrollCount); + + return *this; + } +}; + +typedef std::map<std::string, std::map<int, std::map<int, + OptReportLocationInfo>>> LocationInfoTy; +} // anonymous namespace + +static void collectLocationInfo(yaml::Stream &Stream, + LocationInfoTy &LocationInfo) { + SmallVector<char, 8> Tmp; + + // Note: We're using the YAML parser here directly, instead of using the + // YAMLTraits implementation, because the YAMLTraits implementation does not + // support a way to handle only a subset of the input keys (it will error out + // if there is an input key that you don't map to your class), and + // furthermore, it does not provide a way to handle the Args sequence of + // key/value pairs, where the order must be captured and the 'String' key + // might be repeated. + for (auto &Doc : Stream) { + auto *Root = dyn_cast<yaml::MappingNode>(Doc.getRoot()); + if (!Root) + continue; + + bool Transformed = Root->getRawTag() == "!Passed"; + std::string Pass, File; + int Line = 0, Column = 1; + + int VectorizationFactor = 1; + int InterleaveCount = 1; + int UnrollCount = 1; + + for (auto &RootChild : *Root) { + auto *Key = dyn_cast<yaml::ScalarNode>(RootChild.getKey()); + if (!Key) + continue; + StringRef KeyName = Key->getValue(Tmp); + if (KeyName == "Pass") { + auto *Value = dyn_cast<yaml::ScalarNode>(RootChild.getValue()); + if (!Value) + continue; + Pass = Value->getValue(Tmp); + } else if (KeyName == "DebugLoc") { + auto *DebugLoc = dyn_cast<yaml::MappingNode>(RootChild.getValue()); + if (!DebugLoc) + continue; + + for (auto &DLChild : *DebugLoc) { + auto *DLKey = dyn_cast<yaml::ScalarNode>(DLChild.getKey()); + if (!DLKey) + continue; + StringRef DLKeyName = DLKey->getValue(Tmp); + if (DLKeyName == "File") { + auto *Value = dyn_cast<yaml::ScalarNode>(DLChild.getValue()); + if (!Value) + continue; + File = Value->getValue(Tmp); + } else if (DLKeyName == "Line") { + auto *Value = dyn_cast<yaml::ScalarNode>(DLChild.getValue()); + if (!Value) + continue; + Value->getValue(Tmp).getAsInteger(10, Line); + } else if (DLKeyName == "Column") { + auto *Value = dyn_cast<yaml::ScalarNode>(DLChild.getValue()); + if (!Value) + continue; + Value->getValue(Tmp).getAsInteger(10, Column); + } + } + } else if (KeyName == "Args") { + auto *Args = dyn_cast<yaml::SequenceNode>(RootChild.getValue()); + if (!Args) + continue; + for (auto &ArgChild : *Args) { + auto *ArgMap = dyn_cast<yaml::MappingNode>(&ArgChild); + if (!ArgMap) + continue; + for (auto &ArgKV : *ArgMap) { + auto *ArgKey = dyn_cast<yaml::ScalarNode>(ArgKV.getKey()); + if (!ArgKey) + continue; + StringRef ArgKeyName = ArgKey->getValue(Tmp); + if (ArgKeyName == "VectorizationFactor") { + auto *Value = dyn_cast<yaml::ScalarNode>(ArgKV.getValue()); + if (!Value) + continue; + Value->getValue(Tmp).getAsInteger(10, VectorizationFactor); + } else if (ArgKeyName == "InterleaveCount") { + auto *Value = dyn_cast<yaml::ScalarNode>(ArgKV.getValue()); + if (!Value) + continue; + Value->getValue(Tmp).getAsInteger(10, InterleaveCount); + } else if (ArgKeyName == "UnrollCount") { + auto *Value = dyn_cast<yaml::ScalarNode>(ArgKV.getValue()); + if (!Value) + continue; + Value->getValue(Tmp).getAsInteger(10, UnrollCount); + } + } + } + } + } + + if (Line < 1 || File.empty()) + continue; + + // We track information on both actual and potential transformations. This + // way, if there are multiple possible things on a line that are, or could + // have been transformed, we can indicate that explicitly in the output. + auto UpdateLLII = [Transformed, VectorizationFactor, + InterleaveCount, + UnrollCount](OptReportLocationInfo &LI, + OptReportLocationItemInfo &LLII) { + LLII.Analyzed = true; + if (Transformed) { + LLII.Transformed = true; + + LI.VectorizationFactor = VectorizationFactor; + LI.InterleaveCount = InterleaveCount; + LI.UnrollCount = UnrollCount; + } + }; + + if (Pass == "inline") { + auto &LI = LocationInfo[File][Line][Column]; + UpdateLLII(LI, LI.Inlined); + } else if (Pass == "loop-unroll") { + auto &LI = LocationInfo[File][Line][Column]; + UpdateLLII(LI, LI.Unrolled); + } else if (Pass == "loop-vectorize") { + auto &LI = LocationInfo[File][Line][Column]; + UpdateLLII(LI, LI.Vectorized); + } + } +} + +static bool readLocationInfo(LocationInfoTy &LocationInfo) { + ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = + MemoryBuffer::getFileOrSTDIN(InputFileName); + if (std::error_code EC = Buf.getError()) { + errs() << "error: Can't open file " << InputFileName << ": " << + EC.message() << "\n"; + return false; + } + + SourceMgr SM; + yaml::Stream Stream(Buf.get()->getBuffer(), SM); + collectLocationInfo(Stream, LocationInfo); + + return true; +} + +static bool writeReport(LocationInfoTy &LocationInfo) { + std::error_code EC; + llvm::raw_fd_ostream OS(OutputFileName, EC, + llvm::sys::fs::F_Text); + if (EC) { + errs() << "error: Can't open file " << OutputFileName << ": " << + EC.message() << "\n"; + return false; + } + + bool FirstFile = true; + for (auto &FI : LocationInfo) { + SmallString<128> FileName(FI.first); + if (!InputRelDir.empty()) { + if (std::error_code EC = sys::fs::make_absolute(InputRelDir, FileName)) { + errs() << "error: Can't resolve file path to " << FileName << ": " << + EC.message() << "\n"; + return false; + } + } + + const auto &FileInfo = FI.second; + + ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = + MemoryBuffer::getFile(FileName); + if (std::error_code EC = Buf.getError()) { + errs() << "error: Can't open file " << FileName << ": " << + EC.message() << "\n"; + return false; + } + + if (FirstFile) + FirstFile = false; + else + OS << "\n"; + + OS << "< " << FileName << "\n"; + + // Figure out how many characters we need for the vectorization factors + // and similar. + OptReportLocationInfo MaxLI; + for (auto &FI : FileInfo) + for (auto &LI : FI.second) + MaxLI |= LI.second; + + unsigned VFDigits = llvm::utostr(MaxLI.VectorizationFactor).size(); + unsigned ICDigits = llvm::utostr(MaxLI.InterleaveCount).size(); + unsigned UCDigits = llvm::utostr(MaxLI.UnrollCount).size(); + + // Figure out how many characters we need for the line numbers. + int64_t NumLines = 0; + for (line_iterator LI(*Buf.get(), false); LI != line_iterator(); ++LI) + ++NumLines; + + unsigned LNDigits = llvm::utostr(NumLines).size(); + + for (line_iterator LI(*Buf.get(), false); LI != line_iterator(); ++LI) { + int64_t L = LI.line_number(); + OptReportLocationInfo LLI; + + std::map<int, OptReportLocationInfo> ColsInfo; + unsigned InlinedCols = 0, UnrolledCols = 0, VectorizedCols = 0; + + auto LII = FileInfo.find(L); + if (LII != FileInfo.end()) { + const auto &LineInfo = LII->second; + + for (auto &CI : LineInfo) { + int Col = CI.first; + ColsInfo[Col] = CI.second; + InlinedCols += CI.second.Inlined.Analyzed; + UnrolledCols += CI.second.Unrolled.Analyzed; + VectorizedCols += CI.second.Vectorized.Analyzed; + LLI |= CI.second; + } + } + + // We try to keep the output as concise as possible. If only one thing on + // a given line could have been inlined, vectorized, etc. then we can put + // the marker on the source line itself. If there are multiple options + // then we want to distinguish them by placing the marker for each + // transformation on a separate line following the source line. When we + // do this, we use a '^' character to point to the appropriate column in + // the source line. + + std::string USpaces(Succinct ? 0 : UCDigits, ' '); + std::string VSpaces(Succinct ? 0 : VFDigits + ICDigits + 1, ' '); + + auto UStr = [UCDigits](OptReportLocationInfo &LLI) { + std::string R; + raw_string_ostream RS(R); + if (!Succinct) + RS << llvm::format_decimal(LLI.UnrollCount, UCDigits); + return RS.str(); + }; + + auto VStr = [VFDigits, + ICDigits](OptReportLocationInfo &LLI) -> std::string { + std::string R; + raw_string_ostream RS(R); + if (!Succinct) + RS << llvm::format_decimal(LLI.VectorizationFactor, VFDigits) << + "," << llvm::format_decimal(LLI.InterleaveCount, ICDigits); + return RS.str(); + }; + + OS << llvm::format_decimal(L + 1, LNDigits) << " "; + OS << (LLI.Inlined.Transformed && InlinedCols < 2 ? "I" : " "); + OS << (LLI.Unrolled.Transformed && UnrolledCols < 2 ? + "U" + UStr(LLI) : " " + USpaces); + OS << (LLI.Vectorized.Transformed && VectorizedCols < 2 ? + "V" + VStr(LLI) : " " + VSpaces); + + OS << " | " << *LI << "\n"; + + for (auto &J : ColsInfo) { + if ((J.second.Inlined.Transformed && InlinedCols > 1) || + (J.second.Unrolled.Transformed && UnrolledCols > 1) || + (J.second.Vectorized.Transformed && VectorizedCols > 1)) { + OS << std::string(LNDigits + 1, ' '); + OS << (J.second.Inlined.Transformed && + InlinedCols > 1 ? "I" : " "); + OS << (J.second.Unrolled.Transformed && + UnrolledCols > 1 ? "U" + UStr(J.second) : " " + USpaces); + OS << (J.second.Vectorized.Transformed && + VectorizedCols > 1 ? "V" + VStr(J.second) : " " + VSpaces); + + OS << " | " << std::string(J.first - 1, ' ') << "^\n"; + } + } + } + } + + return true; +} + +int main(int argc, const char **argv) { + sys::PrintStackTraceOnErrorSignal(argv[0]); + + cl::HideUnrelatedOptions(OptReportCategory); + cl::ParseCommandLineOptions( + argc, argv, + "A tool to generate an optimization report from YAML optimization" + " record files.\n"); + + if (Help) + cl::PrintHelpMessage(); + + LocationInfoTy LocationInfo; + if (!readLocationInfo(LocationInfo)) + return 1; + if (!writeReport(LocationInfo)) + return 1; + + return 0; +} + |