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

Hal Finkel via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 5 15:10:36 PDT 2016


Author: hfinkel
Date: Wed Oct  5 17:10:35 2016
New Revision: 283398

URL: http://llvm.org/viewvc/llvm-project?rev=283398&view=rev
Log:
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

Added:
    llvm/trunk/test/tools/llvm-opt-report/
    llvm/trunk/test/tools/llvm-opt-report/Inputs/
    llvm/trunk/test/tools/llvm-opt-report/Inputs/or.c
    llvm/trunk/test/tools/llvm-opt-report/Inputs/or.h
    llvm/trunk/test/tools/llvm-opt-report/Inputs/or.yaml
    llvm/trunk/test/tools/llvm-opt-report/basic.test
    llvm/trunk/tools/llvm-opt-report/
    llvm/trunk/tools/llvm-opt-report/CMakeLists.txt
    llvm/trunk/tools/llvm-opt-report/OptReport.cpp
Modified:
    llvm/trunk/test/CMakeLists.txt

Modified: llvm/trunk/test/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CMakeLists.txt?rev=283398&r1=283397&r2=283398&view=diff
==============================================================================
--- llvm/trunk/test/CMakeLists.txt (original)
+++ llvm/trunk/test/CMakeLists.txt Wed Oct  5 17:10:35 2016
@@ -49,6 +49,7 @@ set(LLVM_TEST_DEPENDS
           llvm-mcmarkup
           llvm-nm
           llvm-objdump
+          llvm-opt-report
           llvm-pdbdump
           llvm-profdata
           llvm-ranlib

Added: llvm/trunk/test/tools/llvm-opt-report/Inputs/or.c
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-opt-report/Inputs/or.c?rev=283398&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-opt-report/Inputs/or.c (added)
+++ llvm/trunk/test/tools/llvm-opt-report/Inputs/or.c Wed Oct  5 17:10:35 2016
@@ -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();
+}
+

Added: llvm/trunk/test/tools/llvm-opt-report/Inputs/or.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-opt-report/Inputs/or.h?rev=283398&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-opt-report/Inputs/or.h (added)
+++ llvm/trunk/test/tools/llvm-opt-report/Inputs/or.h Wed Oct  5 17:10:35 2016
@@ -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();
+}
+

Added: llvm/trunk/test/tools/llvm-opt-report/Inputs/or.yaml
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-opt-report/Inputs/or.yaml?rev=283398&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-opt-report/Inputs/or.yaml (added)
+++ llvm/trunk/test/tools/llvm-opt-report/Inputs/or.yaml Wed Oct  5 17:10:35 2016
@@ -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:          ')'
+...

Added: llvm/trunk/test/tools/llvm-opt-report/basic.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-opt-report/basic.test?rev=283398&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-opt-report/basic.test (added)
+++ llvm/trunk/test/tools/llvm-opt-report/basic.test Wed Oct  5 17:10:35 2016
@@ -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     | 
+

Added: llvm/trunk/tools/llvm-opt-report/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-opt-report/CMakeLists.txt?rev=283398&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-opt-report/CMakeLists.txt (added)
+++ llvm/trunk/tools/llvm-opt-report/CMakeLists.txt Wed Oct  5 17:10:35 2016
@@ -0,0 +1,6 @@
+set(LLVM_LINK_COMPONENTS Core Object Support)
+
+add_llvm_tool(llvm-opt-report
+  OptReport.cpp
+  )
+

Added: llvm/trunk/tools/llvm-opt-report/OptReport.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-opt-report/OptReport.cpp?rev=283398&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-opt-report/OptReport.cpp (added)
+++ llvm/trunk/tools/llvm-opt-report/OptReport.cpp Wed Oct  5 17:10:35 2016
@@ -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;
+}
+




More information about the llvm-commits mailing list