Index: llvm/tools/opt-viewer/assets/colResizable-1.6.min.js
===================================================================
--- /dev/null
+++ llvm/tools/opt-viewer/assets/colResizable-1.6.min.js
@@ -0,0 +1,3 @@
+// colResizable 1.6 - a jQuery plugin by Alvaro Prieto Lauroba http://www.bacubacu.com/colresizable/
+
+!function(t){var e,i=t(document),r=t("head"),o=null,s={},d=0,n="id",a="px",l="JColResizer",c="JCLRFlex",f=parseInt,h=Math,p=navigator.userAgent.indexOf("Trident/4.0")>0;try{e=sessionStorage}catch(g){}r.append("");var u=function(e,i){var o=t(e);if(o.opt=i,o.mode=i.resizeMode,o.dc=o.opt.disabledColumns,o.opt.disable)return w(o);var a=o.id=o.attr(n)||l+d++;o.p=o.opt.postbackSafe,!o.is("table")||s[a]&&!o.opt.partialRefresh||("e-resize"!==o.opt.hoverCursor&&r.append(""),o.addClass(l).attr(n,a).before('
'),o.g=[],o.c=[],o.w=o.width(),o.gc=o.prev(),o.f=o.opt.fixed,i.marginLeft&&o.gc.css("marginLeft",i.marginLeft),i.marginRight&&o.gc.css("marginRight",i.marginRight),o.cs=f(p?e.cellSpacing||e.currentStyle.borderSpacing:o.css("border-spacing"))||2,o.b=f(p?e.border||e.currentStyle.borderLeftWidth:o.css("border-left-width"))||1,s[a]=o,v(o))},w=function(t){var e=t.attr(n),t=s[e];t&&t.is("table")&&(t.removeClass(l+" "+c).gc.remove(),delete s[e])},v=function(i){var r=i.find(">thead>tr:first>th,>thead>tr:first>td");r.length||(r=i.find(">tbody>tr:first>th,>tr:first>th,>tbody>tr:first>td, >tr:first>td")),r=r.filter(":visible"),i.cg=i.find("col"),i.ln=r.length,i.p&&e&&e[i.id]&&m(i,r),r.each(function(e){var r=t(this),o=-1!=i.dc.indexOf(e),s=t(i.gc.append('
')[0].lastChild);s.append(o?"":i.opt.gripInnerHtml).append('
'),e==i.ln-1&&(s.addClass("JCLRLastGrip"),i.f&&s.html("")),s.bind("touchstart mousedown",J),o?s.addClass("JCLRdisabledGrip"):s.removeClass("JCLRdisabledGrip").bind("touchstart mousedown",J),s.t=i,s.i=e,s.c=r,r.w=r.width(),i.g.push(s),i.c.push(r),r.width(r.w).removeAttr("width"),s.data(l,{i:e,t:i.attr(n),last:e==i.ln-1})}),i.cg.removeAttr("width"),i.find("td, th").not(r).not("table th, table td").each(function(){t(this).removeAttr("width")}),i.f||i.removeAttr("width").addClass(c),C(i)},m=function(t,i){var r,o,s=0,d=0,n=[];if(i){if(t.cg.removeAttr("width"),t.opt.flush)return void(e[t.id]="");for(r=e[t.id].split(";"),o=r[t.ln+1],!t.f&&o&&(t.width(o*=1),t.opt.overflow&&(t.css("min-width",o+a),t.w=o));d*{cursor:"+n.opt.dragCursor+"!important}"),a.addClass(n.opt.draggingClass),o=a,n.c[d.i].l)for(var f,h=0;h> style.css */
+
+.hll { background-color: #ffffcc }
+.c { color: #888888 } /* Comment */
+.err { color: #FF0000; background-color: #FFAAAA } /* Error */
+.k { color: #008800; font-weight: bold } /* Keyword */
+.o { color: #333333 } /* Operator */
+.ch { color: #888888 } /* Comment.Hashbang */
+.cm { color: #888888 } /* Comment.Multiline */
+.cp { color: #557799 } /* Comment.Preproc */
+.cpf { color: #888888 } /* Comment.PreprocFile */
+.c1 { color: #888888 } /* Comment.Single */
+.cs { color: #cc0000; font-weight: bold } /* Comment.Special */
+.gd { color: #A00000 } /* Generic.Deleted */
+.ge { font-style: italic } /* Generic.Emph */
+.gr { color: #FF0000 } /* Generic.Error */
+.gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.gi { color: #00A000 } /* Generic.Inserted */
+.go { color: #888888 } /* Generic.Output */
+.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
+.gs { font-weight: bold } /* Generic.Strong */
+.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.gt { color: #0044DD } /* Generic.Traceback */
+.kc { color: #008800; font-weight: bold } /* Keyword.Constant */
+.kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
+.kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
+.kp { color: #003388; font-weight: bold } /* Keyword.Pseudo */
+.kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
+.kt { color: #333399; font-weight: bold } /* Keyword.Type */
+.m { color: #6600EE; font-weight: bold } /* Literal.Number */
+.s { background-color: #fff0f0 } /* Literal.String */
+.na { color: #0000CC } /* Name.Attribute */
+.nb { color: #007020 } /* Name.Builtin */
+.nc { color: #BB0066; font-weight: bold } /* Name.Class */
+.no { color: #003366; font-weight: bold } /* Name.Constant */
+.nd { color: #555555; font-weight: bold } /* Name.Decorator */
+.ni { color: #880000; font-weight: bold } /* Name.Entity */
+.ne { color: #FF0000; font-weight: bold } /* Name.Exception */
+.nf { color: #0066BB; font-weight: bold } /* Name.Function */
+.nl { color: #997700; font-weight: bold } /* Name.Label */
+.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
+.nt { color: #007700 } /* Name.Tag */
+.nv { color: #996633 } /* Name.Variable */
+.ow { color: #000000; font-weight: bold } /* Operator.Word */
+.w { color: #bbbbbb } /* Text.Whitespace */
+.mb { color: #6600EE; font-weight: bold } /* Literal.Number.Bin */
+.mf { color: #6600EE; font-weight: bold } /* Literal.Number.Float */
+.mh { color: #005588; font-weight: bold } /* Literal.Number.Hex */
+.mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
+.mo { color: #4400EE; font-weight: bold } /* Literal.Number.Oct */
+.sb { background-color: #fff0f0 } /* Literal.String.Backtick */
+.sc { color: #0044DD } /* Literal.String.Char */
+.sd { color: #DD4422 } /* Literal.String.Doc */
+.s2 { background-color: #fff0f0 } /* Literal.String.Double */
+.se { color: #666666; font-weight: bold; background-color: #fff0f0 } /* Literal.String.Escape */
+.sh { background-color: #fff0f0 } /* Literal.String.Heredoc */
+.si { background-color: #eeeeee } /* Literal.String.Interpol */
+.sx { color: #DD2200; background-color: #fff0f0 } /* Literal.String.Other */
+.sr { color: #000000; background-color: #fff0ff } /* Literal.String.Regex */
+.s1 { background-color: #fff0f0 } /* Literal.String.Single */
+.ss { color: #AA6600 } /* Literal.String.Symbol */
+.bp { color: #007020 } /* Name.Builtin.Pseudo */
+.vc { color: #336699 } /* Name.Variable.Class */
+.vg { color: #dd7700; font-weight: bold } /* Name.Variable.Global */
+.vi { color: #3333BB } /* Name.Variable.Instance */
+.il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
+
+table#opt_table.dataTable td {
+ font-family: monospace;
+}
+
+table#opt_table_code.dataTable td {
+ padding: 0px;
+ border: 0px;
+}
+
+.back {
+ background-color: #04AA6D;
+ color: white;
+ text-decoration: none;
+ display: inline-block;
+ padding: 8px 16px;
+}
+
+h1.filename-title {
+ text-align:center;
+}
+
+.indent-span {
+ -webkit-box-decoration-break: clone;
+ box-decoration-break: clone;
+ font-family: monospace;
+}
+
+table.dataTable thead th[data-is-resizable=true] {
+ border-left: 1px solid transparent;
+ border-right: 1px dashed #bfbfbf;
+}
+
+table.dataTable thead th.dt-colresizable-hover {
+ cursor: col-resize;
+ background-color: #eaeaea;
+ border-left: 1px solid #bfbfbf;
+ }
+
+table.dataTable thead th.dt-colresizable-bound-min,
+table.dataTable thead th.dt-colresizable-bound-max {
+ opacity: 0.2;
+ cursor: not-allowed !important;
+}
\ No newline at end of file
Index: llvm/tools/opt-viewer/config.yaml
===================================================================
--- /dev/null
+++ llvm/tools/opt-viewer/config.yaml
@@ -0,0 +1,21 @@
+---
+## Opt view config
+
+# Set this to false to disable remark_filters list
+use_remark_filters: true
+
+# Exclude optimization remarks with names matching this regex.
+# Empirical list of remark names (as of clang14):
+# LoopSpillReloadCopies, LoadWithLoopInvariantAddressInvalidated, MissedDetails, LoadClobbered,
+# NotBeneficial, SpillReloadCopies, NotPossible, NoDefinition, NeverInline, UnsupportedIrreducibleCFG,
+# VectorizationNotBeneficial, LoadWithLoopInvariantAddressCondExecuted, HorSLPNotBeneficial, TooCostly
+# LoopMayAccessStore
+exclude_names: NeverInline|NotPossible|LoopSpillReloadCopies|SpillReloadCopies
+# Exclude optimization remarks with text matching this regex:
+exclude_text: ^std\:\:|^__dynamic_cast|^operator new|^operator delete|^__cxa|^__clang|^__cxx
+
+# Collect all optimization remarks, not just failures
+collect_opt_success: False
+
+# Annotate all files, including system headers
+annotate_external: false
Index: llvm/tools/opt-viewer/config_parser.py
===================================================================
--- /dev/null
+++ llvm/tools/opt-viewer/config_parser.py
@@ -0,0 +1,16 @@
+import yaml
+import re
+
+def parse(f):
+ "Parse config file"
+ # TODO - add scheme; currently we just take whatever is there.
+ config = yaml.safe_load(f)
+ if config.get('use_remark_filters', True) and 'remark_filters' in config and len(config['remark_filters']) > 0:
+ # config wants a single regex, transform to a single expression
+ try:
+ regexes = [re.compile(x) for x in config['remark_filters']]
+ except Exception as ex:
+ raise Exception(f"Failed to parse regex in remarks_filters config. Details {ex}")
+ config['remark_filter'] = '|'.join(x.pattern for x in regexes)
+
+ return config
Index: llvm/tools/opt-viewer/cpp_optimization_example/.gitignore
===================================================================
--- /dev/null
+++ llvm/tools/opt-viewer/cpp_optimization_example/.gitignore
@@ -0,0 +1,6 @@
+example
+*.o
+yaml_optimization_remarks
+.depend
+.vscode
+html_output
Index: llvm/tools/opt-viewer/cpp_optimization_example/Makefile
===================================================================
--- /dev/null
+++ llvm/tools/opt-viewer/cpp_optimization_example/Makefile
@@ -0,0 +1,43 @@
+CC=clang++
+CXX=clang++
+RM=rm -f
+MKDIR_P=mkdir -p
+OBJS_DIR=objs
+DEPENDS_FILE=${OBJS_DIR}/.depend
+YAML_OUTPUT_DIR=yaml_optimization_remarks
+OPTIMIZATION_RECORD_FLAGS=-fsave-optimization-record -foptimization-record-file=./${YAML_OUTPUT_DIR}/$(patsubst %.o,%.opt.yaml,$(notdir $@))
+CPPFLAGS=-O3 -std=c++17
+LDFLAGS=
+LDLIBS=
+BINARY_NAME=example
+
+SRCS=$(wildcard *.cc)
+OBJS=$(addprefix $(OBJS_DIR)/,$(subst .cc,.o,$(SRCS)))
+
+.PHONY: output_folder
+
+all: output_folder ${BINARY_NAME}
+
+directories: ${YAML_OUTPUT_DIR} ${OBJS_DIR}
+
+${YAML_OUTPUT_DIR} ${OBJS_DIR}:
+ ${MKDIR_P} $@
+
+${BINARY_NAME}: $(OBJS)
+ $(CXX) $(LDFLAGS) -o $(BINARY_NAME) $(OBJS) $(LDLIBS)
+
+${OBJS_DIR}/%.o : %.cc
+ $(CXX) -c $(CFLAGS) $(CPPFLAGS) $(OPTIMIZATION_RECORD_FLAGS) $< -o $@
+
+depend: $(DEPENDS_FILE)
+
+$(DEPENDS_FILE): $(SRCS)
+ $(RM) $(DEPENDS_FILE)
+ $(MKDIR_P) ${YAML_OUTPUT_DIR} ${OBJS_DIR}
+ $(CXX) $(CPPFLAGS) -MM $^ >> $(DEPENDS_FILE)
+
+include $(DEPENDS_FILE)
+
+clean:
+ $(RM) -r ${YAML_OUTPUT_DIR} ${OBJS_DIR}
+
Index: llvm/tools/opt-viewer/cpp_optimization_example/main.cc
===================================================================
--- /dev/null
+++ llvm/tools/opt-viewer/cpp_optimization_example/main.cc
@@ -0,0 +1,21 @@
+#include
+#include
+
+// Based on Roi Barkan - "Argument passing, core guidelines and concepts" - https://www.youtube.com/watch?v=uylFACqcWYI
+void scale_down(std::vector& v, const double& a) {
+ for (auto& item : v) {
+ item /= a;
+ }
+}
+
+void scale_down_example() {
+ std::vector v {2, 1, 2, 3, 4};
+ scale_down(v, v[0]);
+
+}
+
+int main() {
+ scale_down_example();
+
+ return 0;
+}
Index: llvm/tools/opt-viewer/cpp_optimization_example/run_optview2.sh
===================================================================
--- /dev/null
+++ llvm/tools/opt-viewer/cpp_optimization_example/run_optview2.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+set -euo pipefail
+cd "$(dirname "$0")" || exit 1
+
+echo "Running make..."
+make
+
+echo "Running optview2..."
+../opt-viewer.py --open-browser --output-dir ./html_output --source-dir ./ ./yaml_optimization_remarks
Index: llvm/tools/opt-viewer/opt-viewer.py
===================================================================
--- llvm/tools/opt-viewer/opt-viewer.py
+++ llvm/tools/opt-viewer/opt-viewer.py
@@ -1,25 +1,24 @@
-#!/usr/bin/env python
-
-from __future__ import print_function
+#!/usr/bin/env python3
import argparse
-import errno
import functools
-import html
-import io
-from multiprocessing import cpu_count
import os.path
import re
import shutil
import sys
-
+import json
+import glob
+import pathlib
+import collections
+from datetime import datetime
from pygments import highlight
from pygments.lexers.c_cpp import CppLexer
from pygments.formatters import HtmlFormatter
-
import optpmap
import optrecord
-
+import config_parser
+import logging
+logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
desc = '''Generate HTML output to visualize optimization records from the YAML files
generated with -fsave-optimization-record and -fdiagnostics-show-hotness.
@@ -35,123 +34,137 @@
context = Context()
-def suppress(remark):
- if remark.Name == 'sil.Specialized':
- return remark.getArgDict()['Function'][0].startswith('\"Swift.')
- elif remark.Name == 'sil.Inlined':
- return remark.getArgDict()['Callee'][0].startswith(('\"Swift.', '\"specialized Swift.'))
- return False
-
-class SourceFileRenderer:
- def __init__(self, source_dir, output_dir, filename, no_highlight):
- self.filename = filename
- existing_filename = None
- if os.path.exists(filename):
- existing_filename = filename
- else:
- fn = os.path.join(source_dir, filename)
- if os.path.exists(fn):
- existing_filename = fn
-
- self.no_highlight = no_highlight
- self.stream = io.open(os.path.join(output_dir, optrecord.html_file_name(filename)), 'w', encoding='utf-8')
- if existing_filename:
- self.source_stream = io.open(existing_filename, encoding='utf-8')
- else:
- self.source_stream = None
- print(u'''
-
-Unable to locate file {}
-
- '''.format(filename), file=self.stream)
- self.html_formatter = HtmlFormatter(encoding='utf-8')
- self.cpp_lexer = CppLexer(stripnl=False)
+def render_file_source(source_dir, output_dir, filename, line_remarks):
+ html_filename = os.path.join(output_dir, optrecord.html_file_name(filename))
+ filename = filename if os.path.exists(filename) else os.path.join(source_dir, filename)
- def render_source_lines(self, stream, line_remarks):
+ html_formatter = HtmlFormatter(encoding='utf-8')
+ cpp_lexer = CppLexer(stripnl=False)
+
+ def render_source_lines(stream, line_remarks):
file_text = stream.read()
- if self.no_highlight:
- html_highlighted = file_text
- else:
- html_highlighted = highlight(
+ html_highlighted = highlight(
file_text,
- self.cpp_lexer,
- self.html_formatter)
+ cpp_lexer,
+ html_formatter)
- # Note that the API is different between Python 2 and 3. On
- # Python 3, pygments.highlight() returns a bytes object, so we
- # have to decode. On Python 2, the output is str but since we
- # support unicode characters and the output streams is unicode we
- # decode too.
- html_highlighted = html_highlighted.decode('utf-8')
+ # Note that the API is different between Python 2 and 3. On
+ # Python 3, pygments.highlight() returns a bytes object, so we
+ # have to decode. On Python 2, the output is str but since we
+ # support unicode characters and the output streams is unicode we
+ # decode too.
+ html_highlighted = html_highlighted.decode('utf-8')
- # Take off the header and footer, these must be
- # reapplied line-wise, within the page structure
- html_highlighted = html_highlighted.replace('', '')
- html_highlighted = html_highlighted.replace(' ', '')
+ # Take off the header and footer, these must be
+ # reapplied line-wise, within the page structure
+ html_highlighted = html_highlighted.replace('', '')
+ html_highlighted = html_highlighted.replace(' ', '')
for (linenum, html_line) in enumerate(html_highlighted.split('\n'), start=1):
- print(u'''
-
-{linenum}
-
-
-
- '''.format(**locals()), file=self.stream)
-
- for remark in line_remarks.get(linenum, []):
- if not suppress(remark):
- self.render_inline_remarks(remark, html_line)
-
- def render_inline_remarks(self, r, line):
- inlining_context = r.DemangledFunctionName
- dl = context.caller_loc.get(r.Function)
+ yield [f'{linenum} ', '', '', f'', '']
+
+ cur_line_remarks = line_remarks.get(linenum, [])
+ from collections import defaultdict
+ d = defaultdict(list)
+ count_deleted = defaultdict(int)
+ for obj in cur_line_remarks:
+ if len(d[obj.Name]) < 5:
+ d[obj.Name].append(obj)
+ else:
+ count_deleted[obj.Name] += 1
+
+ for obj_name, remarks in d.items():
+ # render caret line, if all rendered remarks share a column
+ columns = [r.Column for r in remarks]
+ if all(c == columns[0] for c in columns) and columns[0] != 0:
+ yield ['',
+ 0,
+ {'class': f"column-entry-yellow", 'text': ''},
+ {'class': 'column-entry-yellow',
+ 'text': f'''{" "*(columns[0]-1) + '^'} '''},
+ {'class': f"column-entry-yellow", 'text': ''},
+ ]
+ for remark in remarks:
+ yield render_inline_remark(remark, html_line)
+ if count_deleted[obj_name] != 0:
+ yield ['',
+ 0,
+ {'class': f"column-entry-yellow", 'text': ''},
+ {'class': 'column-entry-yellow', 'text': f'''...{count_deleted[obj_name]} similar remarks omitted. '''},
+ {'class': f"column-entry-yellow", 'text': ''},
+ ]
+
+
+ def render_inline_remark(remark, line):
+ inlining_context = remark.DemangledFunctionName
+ dl = context.caller_loc.get(remark.Function)
if dl:
dl_dict = dict(list(dl))
link = optrecord.make_link(dl_dict['File'], dl_dict['Line'] - 2)
- inlining_context = "{r.DemangledFunctionName} ".format(**locals())
+ inlining_context = f"{remark.DemangledFunctionName} "
- # Column is the number of characters *including* tabs, keep those and
- # replace everything else with spaces.
- indent = line[:max(r.Column, 1) - 1]
- indent = re.sub('\S', ' ', indent)
+ start_line = re.sub("^", "", line)
+ spaces = len(start_line) - len(start_line.lstrip())
+ indent = f"{spaces + 2}ch"
# Create expanded message and link if we have a multiline message.
- lines = r.message.split('\n')
+ lines = remark.message.split('\n')
if len(lines) > 1:
expand_link = '+ '
message = lines[0]
- expand_message = u'''
+ other_lines = "\n".join(lines[1:])
+ expand_message = f'''
'''.format(indent, '\n'.join(lines[1:]))
+
+'''
else:
expand_link = ''
expand_message = ''
- message = r.message
- print(u'''
-
-
-{r.RelativeHotness}
-{r.PassWithDiffPrefix}
-{indent} {expand_link} {message} {expand_message}
-{inlining_context}
- '''.format(**locals()), file=self.stream)
-
- def render(self, line_remarks):
- if not self.source_stream:
+ message = remark.message
+ return ['',
+ remark.RelativeHotness,
+ {'class': f"column-entry-{remark.color}", 'text': remark.PassWithDiffPrefix},
+ {'class': 'column-entry-yellow', 'text': f'''• {expand_link} {message} {expand_message}'''},
+ {'class': f"column-entry-yellow", 'text': inlining_context},
+ ]
+
+ with open(html_filename, "w", encoding='utf-8') as f:
+ if not os.path.exists(filename):
+ f.write(f'''
+
+ Unable to locate file {filename}
+''')
return
- print(u'''
+ try:
+ with open(filename, encoding="utf8", errors='ignore') as source_stream:
+ entries = list(render_source_lines(source_stream, line_remarks))
+ except Exception:
+ print(f"Failed to process file {filename}")
+ raise
+
+ f.write(f'''
-{}
-
+{os.path.basename(filename)}
+
+
+
+
+
+
+
+{os.path.abspath(filename)}
+Back
+
+Back
+
-
-
-
-
-
-
-Line
- Hotness
- Optimization
- Source
- Inline Context
-
-
-'''.format(os.path.basename(self.filename)), file=self.stream)
- self.render_source_lines(self.source_stream, line_remarks)
-
- print(u'''
-
-
-''', file=self.stream)
-
-
-class IndexRenderer:
- def __init__(self, output_dir, should_display_hotness, max_hottest_remarks_on_index):
- self.stream = io.open(os.path.join(output_dir, 'index.html'), 'w', encoding='utf-8')
- self.should_display_hotness = should_display_hotness
- self.max_hottest_remarks_on_index = max_hottest_remarks_on_index
-
- def render_entry(self, r, odd):
- escaped_name = html.escape(r.DemangledFunctionName)
- print(u'''
-
-{r.DebugLocString}
-{r.RelativeHotness}
-{escaped_name}
-{r.PassWithDiffPrefix}
- '''.format(**locals()), file=self.stream)
-
- def render(self, all_remarks):
- print(u'''
+