Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -49,6 +49,7 @@ llvm-mcmarkup llvm-nm llvm-objdump + llvm-opt-report llvm-pdbdump llvm-profdata llvm-ranlib Index: test/tools/llvm-opt-report/Inputs/or.h =================================================================== --- /dev/null +++ test/tools/llvm-opt-report/Inputs/or.h @@ -0,0 +1,17 @@ +void TestH(int *res, int *c, int *d, int *p, int n) { + int i; + +#pragma clang loop vectorize(assume_safety) + for (i = 0; i < 1600; i++) { + res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; + } + + for (i = 0; i < 16; i++) { + res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; + } + + foo(); + + foo(); bar(); foo(); +} + Index: test/tools/llvm-opt-report/Inputs/or.c =================================================================== --- /dev/null +++ test/tools/llvm-opt-report/Inputs/or.c @@ -0,0 +1,22 @@ +void bar(); +void foo() { bar(); } + +#include "or.h" + +void Test(int *res, int *c, int *d, int *p, int n) { + int i; + +#pragma clang loop vectorize(assume_safety) + for (i = 0; i < 1600; i++) { + res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; + } + + for (i = 0; i < 16; i++) { + res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; + } + + foo(); + + foo(); bar(); foo(); +} + Index: test/tools/llvm-opt-report/Inputs/or.yaml =================================================================== --- /dev/null +++ test/tools/llvm-opt-report/Inputs/or.yaml @@ -0,0 +1,227 @@ +--- !Missed +Pass: inline +Name: NoDefinition +DebugLoc: { File: Inputs/or.c, Line: 2, Column: 14 } +Function: foo +Args: + - Callee: bar + - String: ' will not be inlined into ' + - Caller: foo + - String: ' because its definition is unavailable' +... +--- !Missed +Pass: inline +Name: NoDefinition +DebugLoc: { File: Inputs/or.h, Line: 15, Column: 10 } +Function: TestH +Args: + - Callee: bar + - String: ' will not be inlined into ' + - Caller: TestH + - String: ' because its definition is unavailable' +... +--- !Analysis +Pass: inline +Name: CanBeInlined +DebugLoc: { File: Inputs/or.h, Line: 13, Column: 3 } +Function: TestH +Args: + - Callee: foo + - String: ' can be inlined into ' + - Caller: TestH + - String: ' with cost=' + - Cost: '30' + - String: ' (threshold=' + - Threshold: '412' + - String: ')' +... +--- !Passed +Pass: inline +Name: Inlined +DebugLoc: { File: Inputs/or.h, Line: 13, Column: 3 } +Function: TestH +Args: + - Callee: foo + - String: ' inlined into ' + - Caller: TestH +... +--- !Analysis +Pass: inline +Name: CanBeInlined +DebugLoc: { File: Inputs/or.h, Line: 15, Column: 3 } +Function: TestH +Args: + - Callee: foo + - String: ' can be inlined into ' + - Caller: TestH + - String: ' with cost=' + - Cost: '30' + - String: ' (threshold=' + - Threshold: '412' + - String: ')' +... +--- !Passed +Pass: inline +Name: Inlined +DebugLoc: { File: Inputs/or.h, Line: 15, Column: 3 } +Function: TestH +Args: + - Callee: foo + - String: ' inlined into ' + - Caller: TestH +... +--- !Analysis +Pass: inline +Name: CanBeInlined +DebugLoc: { File: Inputs/or.h, Line: 15, Column: 17 } +Function: TestH +Args: + - Callee: foo + - String: ' can be inlined into ' + - Caller: TestH + - String: ' with cost=' + - Cost: '30' + - String: ' (threshold=' + - Threshold: '412' + - String: ')' +... +--- !Passed +Pass: inline +Name: Inlined +DebugLoc: { File: Inputs/or.h, Line: 15, Column: 17 } +Function: TestH +Args: + - Callee: foo + - String: ' inlined into ' + - Caller: TestH +... +--- !Passed +Pass: loop-unroll +Name: FullyUnrolled +DebugLoc: { File: Inputs/or.h, Line: 9, Column: 3 } +Function: TestH +Args: + - String: 'completely unrolled loop with ' + - UnrollCount: '16' + - String: ' iterations' +... +--- !Missed +Pass: inline +Name: NoDefinition +DebugLoc: { File: Inputs/or.c, Line: 20, Column: 10 } +Function: Test +Args: + - Callee: bar + - String: ' will not be inlined into ' + - Caller: Test + - String: ' because its definition is unavailable' +... +--- !Analysis +Pass: inline +Name: CanBeInlined +DebugLoc: { File: Inputs/or.c, Line: 18, Column: 3 } +Function: Test +Args: + - Callee: foo + - String: ' can be inlined into ' + - Caller: Test + - String: ' with cost=' + - Cost: '30' + - String: ' (threshold=' + - Threshold: '412' + - String: ')' +... +--- !Passed +Pass: inline +Name: Inlined +DebugLoc: { File: Inputs/or.c, Line: 18, Column: 3 } +Function: Test +Args: + - Callee: foo + - String: ' inlined into ' + - Caller: Test +... +--- !Analysis +Pass: inline +Name: CanBeInlined +DebugLoc: { File: Inputs/or.c, Line: 20, Column: 3 } +Function: Test +Args: + - Callee: foo + - String: ' can be inlined into ' + - Caller: Test + - String: ' with cost=' + - Cost: '30' + - String: ' (threshold=' + - Threshold: '412' + - String: ')' +... +--- !Passed +Pass: inline +Name: Inlined +DebugLoc: { File: Inputs/or.c, Line: 20, Column: 3 } +Function: Test +Args: + - Callee: foo + - String: ' inlined into ' + - Caller: Test +... +--- !Analysis +Pass: inline +Name: CanBeInlined +DebugLoc: { File: Inputs/or.c, Line: 20, Column: 17 } +Function: Test +Args: + - Callee: foo + - String: ' can be inlined into ' + - Caller: Test + - String: ' with cost=' + - Cost: '30' + - String: ' (threshold=' + - Threshold: '412' + - String: ')' +... +--- !Passed +Pass: inline +Name: Inlined +DebugLoc: { File: Inputs/or.c, Line: 20, Column: 17 } +Function: Test +Args: + - Callee: foo + - String: ' inlined into ' + - Caller: Test +... +--- !Passed +Pass: loop-unroll +Name: FullyUnrolled +DebugLoc: { File: Inputs/or.c, Line: 14, Column: 3 } +Function: Test +Args: + - String: 'completely unrolled loop with ' + - UnrollCount: '16' + - String: ' iterations' +... +--- !Passed +Pass: loop-vectorize +Name: Vectorized +DebugLoc: { File: Inputs/or.h, Line: 5, Column: 3 } +Function: TestH +Args: + - String: 'vectorized loop (vectorization width: ' + - VectorizationFactor: '4' + - String: ', interleaved count: ' + - InterleaveCount: '2' + - String: ')' +... +--- !Passed +Pass: loop-vectorize +Name: Vectorized +DebugLoc: { File: Inputs/or.c, Line: 10, Column: 3 } +Function: Test +Args: + - String: 'vectorized loop (vectorization width: ' + - VectorizationFactor: '4' + - String: ', interleaved count: ' + - InterleaveCount: '2' + - String: ')' +... Index: test/tools/llvm-opt-report/basic.test =================================================================== --- /dev/null +++ test/tools/llvm-opt-report/basic.test @@ -0,0 +1,97 @@ +RUN: llvm-opt-report -r %p %p/Inputs/or.yaml | FileCheck -strict-whitespace %s +RUN: llvm-opt-report -s -r %p %p/Inputs/or.yaml | FileCheck -strict-whitespace -check-prefix=CHECK-SUCCINCT %s + +; CHECK: < {{.*}}/Inputs/or.c +; CHECK-NEXT: 2 | void bar(); +; CHECK-NEXT: 3 | void foo() { bar(); } +; CHECK-NEXT: 4 | +; CHECK-NEXT: 5 | #include "or.h" +; CHECK-NEXT: 6 | +; CHECK-NEXT: 7 | void Test(int *res, int *c, int *d, int *p, int n) { +; CHECK-NEXT: 8 | int i; +; CHECK-NEXT: 9 | +; CHECK-NEXT: 10 | #pragma clang loop vectorize(assume_safety) +; CHECK-NEXT: 11 V4,2 | for (i = 0; i < 1600; i++) { +; CHECK-NEXT: 12 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; +; CHECK-NEXT: 13 | } +; CHECK-NEXT: 14 | +; CHECK-NEXT: 15 U16 | for (i = 0; i < 16; i++) { +; CHECK-NEXT: 16 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; +; CHECK-NEXT: 17 | } +; CHECK-NEXT: 18 | +; CHECK-NEXT: 19 I | foo(); +; CHECK-NEXT: 20 | +; CHECK-NEXT: 21 | foo(); bar(); foo(); +; CHECK-NEXT: I | ^ +; CHECK-NEXT: I | ^ +; CHECK-NEXT: 22 | } +; CHECK-NEXT: 23 | + +; CHECK: < {{.*}}/Inputs/or.h +; CHECK-NEXT: 2 | void TestH(int *res, int *c, int *d, int *p, int n) { +; CHECK-NEXT: 3 | int i; +; CHECK-NEXT: 4 | +; CHECK-NEXT: 5 | #pragma clang loop vectorize(assume_safety) +; CHECK-NEXT: 6 V4,2 | for (i = 0; i < 1600; i++) { +; CHECK-NEXT: 7 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; +; CHECK-NEXT: 8 | } +; CHECK-NEXT: 9 | +; CHECK-NEXT: 10 U16 | for (i = 0; i < 16; i++) { +; CHECK-NEXT: 11 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; +; CHECK-NEXT: 12 | } +; CHECK-NEXT: 13 | +; CHECK-NEXT: 14 I | foo(); +; CHECK-NEXT: 15 | +; CHECK-NEXT: 16 | foo(); bar(); foo(); +; CHECK-NEXT: I | ^ +; CHECK-NEXT: I | ^ +; CHECK-NEXT: 17 | } +; CHECK-NEXT: 18 | + +; CHECK-SUCCINCT: < {{.*}}/Inputs/or.c +CHECK-SUCCINCT-NEXT: 2 | void bar(); +CHECK-SUCCINCT-NEXT: 3 | void foo() { bar(); } +CHECK-SUCCINCT-NEXT: 4 | +CHECK-SUCCINCT-NEXT: 5 | #include "or.h" +CHECK-SUCCINCT-NEXT: 6 | +CHECK-SUCCINCT-NEXT: 7 | void Test(int *res, int *c, int *d, int *p, int n) { +CHECK-SUCCINCT-NEXT: 8 | int i; +CHECK-SUCCINCT-NEXT: 9 | +CHECK-SUCCINCT-NEXT: 10 | #pragma clang loop vectorize(assume_safety) +CHECK-SUCCINCT-NEXT: 11 V | for (i = 0; i < 1600; i++) { +CHECK-SUCCINCT-NEXT: 12 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; +CHECK-SUCCINCT-NEXT: 13 | } +CHECK-SUCCINCT-NEXT: 14 | +CHECK-SUCCINCT-NEXT: 15 U | for (i = 0; i < 16; i++) { +CHECK-SUCCINCT-NEXT: 16 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; +CHECK-SUCCINCT-NEXT: 17 | } +CHECK-SUCCINCT-NEXT: 18 | +CHECK-SUCCINCT-NEXT: 19 I | foo(); +CHECK-SUCCINCT-NEXT: 20 | +CHECK-SUCCINCT-NEXT: 21 | foo(); bar(); foo(); +CHECK-SUCCINCT-NEXT: I | ^ +CHECK-SUCCINCT-NEXT: I | ^ +CHECK-SUCCINCT-NEXT: 22 | } +CHECK-SUCCINCT-NEXT: 23 | + +CHECK-SUCCINCT: < {{.*}}/Inputs/or.h +CHECK-SUCCINCT-NEXT: 2 | void TestH(int *res, int *c, int *d, int *p, int n) { +CHECK-SUCCINCT-NEXT: 3 | int i; +CHECK-SUCCINCT-NEXT: 4 | +CHECK-SUCCINCT-NEXT: 5 | #pragma clang loop vectorize(assume_safety) +CHECK-SUCCINCT-NEXT: 6 V | for (i = 0; i < 1600; i++) { +CHECK-SUCCINCT-NEXT: 7 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; +CHECK-SUCCINCT-NEXT: 8 | } +CHECK-SUCCINCT-NEXT: 9 | +CHECK-SUCCINCT-NEXT: 10 U | for (i = 0; i < 16; i++) { +CHECK-SUCCINCT-NEXT: 11 | res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; +CHECK-SUCCINCT-NEXT: 12 | } +CHECK-SUCCINCT-NEXT: 13 | +CHECK-SUCCINCT-NEXT: 14 I | foo(); +CHECK-SUCCINCT-NEXT: 15 | +CHECK-SUCCINCT-NEXT: 16 | foo(); bar(); foo(); +CHECK-SUCCINCT-NEXT: I | ^ +CHECK-SUCCINCT-NEXT: I | ^ +CHECK-SUCCINCT-NEXT: 17 | } +CHECK-SUCCINCT-NEXT: 18 | + Index: tools/llvm-opt-report/CMakeLists.txt =================================================================== --- /dev/null +++ tools/llvm-opt-report/CMakeLists.txt @@ -0,0 +1,6 @@ +set(LLVM_LINK_COMPONENTS Core Object Support) + +add_llvm_tool(llvm-opt-report + OptReport.cpp + ) + Index: tools/llvm-opt-report/OptReport.cpp =================================================================== --- /dev/null +++ tools/llvm-opt-report/OptReport.cpp @@ -0,0 +1,394 @@ +//===------------------ 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 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 + InputFileName(cl::Positional, cl::desc(""), cl::init("-"), + cl::cat(OptReportCategory)); + +static cl::opt + OutputFileName("o", cl::desc("Output file"), cl::init("-"), + cl::cat(OptReportCategory)); + +static cl::opt + InputRelDir("r", cl::desc("Root for relative input paths"), cl::init(""), + cl::cat(OptReportCategory)); + +static cl::opt + 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>> LocationInfoTy; +} // anonymous namespace + +static void collectLocationInfo(yaml::Stream &Stream, + LocationInfoTy &LocationInfo) { + SmallVector Tmp; + + for (auto &Doc : Stream) { + auto *Root = dyn_cast(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(RootChild.getKey()); + if (!Key) + continue; + StringRef KeyName = Key->getValue(Tmp); + if (KeyName == "Pass") { + auto *Value = dyn_cast(RootChild.getValue()); + if (!Value) + continue; + Pass = Value->getValue(Tmp); + } else if (KeyName == "DebugLoc") { + auto *DebugLoc = dyn_cast(RootChild.getValue()); + if (!DebugLoc) + continue; + + for (auto &DLChild : *DebugLoc) { + auto *DLKey = dyn_cast(DLChild.getKey()); + if (!DLKey) + continue; + StringRef DLKeyName = DLKey->getValue(Tmp); + if (DLKeyName == "File") { + auto *Value = dyn_cast(DLChild.getValue()); + if (!Value) + continue; + File = Value->getValue(Tmp); + } else if (DLKeyName == "Line") { + auto *Value = dyn_cast(DLChild.getValue()); + if (!Value) + continue; + Value->getValue(Tmp).getAsInteger(10, Line); + } else if (DLKeyName == "Column") { + auto *Value = dyn_cast(DLChild.getValue()); + if (!Value) + continue; + Value->getValue(Tmp).getAsInteger(10, Column); + } + } + } else if (KeyName == "Args") { + auto *Args = dyn_cast(RootChild.getValue()); + if (!Args) + continue; + for (auto &ArgChild : *Args) { + auto *ArgMap = dyn_cast(&ArgChild); + if (!ArgMap) + continue; + for (auto &ArgKV : *ArgMap) { + auto *ArgKey = dyn_cast(ArgKV.getKey()); + if (!ArgKey) + continue; + StringRef ArgKeyName = ArgKey->getValue(Tmp); + if (ArgKeyName == "VectorizationFactor") { + auto *Value = dyn_cast(ArgKV.getValue()); + if (!Value) + continue; + Value->getValue(Tmp).getAsInteger(10, VectorizationFactor); + } else if (ArgKeyName == "InterleaveCount") { + auto *Value = dyn_cast(ArgKV.getValue()); + if (!Value) + continue; + Value->getValue(Tmp).getAsInteger(10, InterleaveCount); + } else if (ArgKeyName == "UnrollCount") { + auto *Value = dyn_cast(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> 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> 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 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; +} +