1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-26 04:32:44 +01:00

Add an llvm-opt-report tool to generate basic source-annotated optimization summaries

LLVM now has the ability to record information from optimization remarks in a
machine-consumable YAML file for later analysis. This can be enabled in opt
(see r282539), and D25225 adds a Clang flag to do the same. This patch adds
llvm-opt-report, a tool to generate basic optimization "listing" files
(annotated sources with information about what optimizations were performed)
from one of these YAML inputs.

D19678 proposed to add this capability directly to Clang, but this more-general
YAML-based infrastructure was the direction we decided upon in that review
thread.

For this optimization report, I focused on making the output as succinct as
possible while providing information on inlining and loop transformations. The
goal here is that the source code should still be easily readable in the
report. My primary inspiration here is the reports generated by Cray's tools
(http://docs.cray.com/books/S-2496-4101/html-S-2496-4101/z1112823641oswald.html).
These reports are highly regarded within the HPC community. Intel's compiler,
for example, also has an optimization-report capability
(https://software.intel.com/sites/default/files/managed/55/b1/new-compiler-optimization-reports.pdf).

  $ cat /tmp/v.c
  void bar();
  void foo() { bar(); }

  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();
  }

D25225 adds -fsave-optimization-record (and
-fsave-optimization-record=filename), and this would be used as follows:

  $ clang -O3 -o /tmp/v.o -c /tmp/v.c -fsave-optimization-record
  $ llvm-opt-report /tmp/v.yaml > /tmp/v.lst
  $ cat /tmp/v.lst

  < /tmp/v.c
   2          | void bar();
   3          | void foo() { bar(); }
   4          |
   5          | void Test(int *res, int *c, int *d, int *p, int n) {
   6          |   int i;
   7          |
   8          | #pragma clang loop vectorize(assume_safety)
   9     V4,2 |   for (i = 0; i < 1600; i++) {
  10          |     res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
  11          |   }
  12          |
  13  U16     |   for (i = 0; i < 16; i++) {
  14          |     res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
  15          |   }
  16          |
  17 I        |   foo();
  18          |
  19          |   foo(); bar(); foo();
     I        |   ^
     I        |                 ^
  20          | }

Each source line gets a prefix giving the line number, and a few columns for
important optimizations: inlining, loop unrolling and loop vectorization. An
'I' is printed next to a line where a function was inlined, a 'U' next to an
unrolled loop, and 'V' next to a vectorized loop. These are printed on the
relevant code line when that seems unambiguous, or on subsequent lines when
multiple potential options exist (messages, both positive and negative, from
the same optimization with different column numbers are taken to indicate
potential ambiguity). When on subsequent lines, a '^' is output in the relevant
column.

Annotated source for all relevant input files are put into the listing file
(each starting with '<' and then the file name).

You can disable having the unrolling/vectorization factors appear by using the
-s flag.

Differential Revision: https://reviews.llvm.org/D25262

llvm-svn: 283398
This commit is contained in:
Hal Finkel 2016-10-05 22:10:35 +00:00
parent 96b15fe0ad
commit e0e1eceb55
7 changed files with 771 additions and 0 deletions

View File

@ -49,6 +49,7 @@ set(LLVM_TEST_DEPENDS
llvm-mcmarkup
llvm-nm
llvm-objdump
llvm-opt-report
llvm-pdbdump
llvm-profdata
llvm-ranlib

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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: ')'
...

View File

@ -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 |

View File

@ -0,0 +1,6 @@
set(LLVM_LINK_COMPONENTS Core Object Support)
add_llvm_tool(llvm-opt-report
OptReport.cpp
)

View File

@ -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;
}