Index: llvm/trunk/tools/opt-viewer/CMakeLists.txt =================================================================== --- llvm/trunk/tools/opt-viewer/CMakeLists.txt +++ llvm/trunk/tools/opt-viewer/CMakeLists.txt @@ -0,0 +1,13 @@ +set (files + "opt-diff.py" + "opt-stats.py" + "opt-viewer.py" + "optpmap.py" + "optrecord.py" + "style.css") + +foreach (file ${files}) + install(PROGRAMS ${file} + DESTINATION share/opt-viewer + COMPONENT opt-viewer) +endforeach (file) Index: llvm/trunk/tools/opt-viewer/opt-diff.py =================================================================== --- llvm/trunk/tools/opt-viewer/opt-diff.py +++ llvm/trunk/tools/opt-viewer/opt-diff.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python2.7 + +from __future__ import print_function + +desc = '''Generate the difference of two YAML files into a new YAML file (works on +pair of directories too). A new attribute 'Added' is set to True or False +depending whether the entry is added or removed from the first input to the +next. + +The tools requires PyYAML.''' + +import yaml +# Try to use the C parser. +try: + from yaml import CLoader as Loader +except ImportError: + from yaml import Loader + +import optrecord +import argparse +from collections import defaultdict +from multiprocessing import cpu_count, Pool +import os, os.path +import fnmatch + +def find_files(dir_or_file): + if os.path.isfile(dir_or_file): + return [dir_or_file] + + all = [] + for dir, subdirs, files in os.walk(dir_or_file): + for file in files: + if fnmatch.fnmatch(file, "*.opt.yaml"): + all.append( os.path.join(dir, file)) + return all + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=desc) + parser.add_argument('yaml_dir_or_file_1') + parser.add_argument('yaml_dir_or_file_2') + parser.add_argument( + '--jobs', + '-j', + default=cpu_count(), + type=int, + help='Max job count (defaults to %(default)s, the current CPU count)') + parser.add_argument( + '--no-progress-indicator', + '-n', + action='store_true', + default=False, + help='Do not display any indicator of how many YAML files were read.') + parser.add_argument('--output', '-o', default='diff.opt.yaml') + args = parser.parse_args() + + files1 = find_files(args.yaml_dir_or_file_1) + files2 = find_files(args.yaml_dir_or_file_2) + + print_progress = not args.no_progress_indicator + all_remarks1, _, _ = optrecord.gather_results(files1, args.jobs, print_progress) + all_remarks2, _, _ = optrecord.gather_results(files2, args.jobs, print_progress) + + added = set(all_remarks2.values()) - set(all_remarks1.values()) + removed = set(all_remarks1.values()) - set(all_remarks2.values()) + + for r in added: + r.Added = True + for r in removed: + r.Added = False + with open(args.output, 'w') as stream: + yaml.dump_all(added | removed, stream) Index: llvm/trunk/tools/opt-viewer/opt-stats.py =================================================================== --- llvm/trunk/tools/opt-viewer/opt-stats.py +++ llvm/trunk/tools/opt-viewer/opt-stats.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python2.7 + +from __future__ import print_function + +desc = '''Generate statistics about optimization records from the YAML files +generated with -fsave-optimization-record and -fdiagnostics-show-hotness. + +The tools requires PyYAML and Pygments Python packages.''' + +import optrecord +import argparse +import operator +from collections import defaultdict +from multiprocessing import cpu_count, Pool + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=desc) + parser.add_argument('yaml_files', nargs='+') + parser.add_argument( + '--jobs', + '-j', + default=cpu_count(), + type=int, + help='Max job count (defaults to %(default)s, the current CPU count)') + parser.add_argument( + '--no-progress-indicator', + '-n', + action='store_true', + default=False, + help='Do not display any indicator of how many YAML files were read.') + args = parser.parse_args() + + print_progress = not args.no_progress_indicator + all_remarks, file_remarks, _ = optrecord.gather_results( + args.yaml_files, args.jobs, print_progress) + if print_progress: + print('\n') + + bypass = defaultdict(int) + byname = defaultdict(int) + for r in optrecord.itervalues(all_remarks): + bypass[r.Pass] += 1 + byname[r.Pass + "/" + r.Name] += 1 + + total = len(all_remarks) + print("{:24s} {:10d}\n".format("Total number of remarks", total)) + + print("Top 10 remarks by pass:") + for (passname, count) in sorted(bypass.items(), key=operator.itemgetter(1), + reverse=True)[:10]: + print(" {:30s} {:2.0f}%". format(passname, count * 100. / total)) + + print("\nTop 10 remarks:") + for (name, count) in sorted(byname.items(), key=operator.itemgetter(1), + reverse=True)[:10]: + print(" {:30s} {:2.0f}%". format(name, count * 100. / total)) Index: llvm/trunk/tools/opt-viewer/opt-viewer.py =================================================================== --- llvm/trunk/tools/opt-viewer/opt-viewer.py +++ llvm/trunk/tools/opt-viewer/opt-viewer.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python2.7 + +from __future__ import print_function + +import argparse +import cgi +import errno +import functools +from multiprocessing import cpu_count +import os.path +import re +import shutil + +from pygments import highlight +from pygments.lexers.c_cpp import CppLexer +from pygments.formatters import HtmlFormatter + +import optpmap +import optrecord + + +desc = '''Generate HTML output to visualize optimization records from the YAML files +generated with -fsave-optimization-record and -fdiagnostics-show-hotness. + +The tools requires PyYAML and Pygments Python packages.''' + + +# This allows passing the global context to the child processes. +class Context: + def __init__(self, caller_loc = dict()): + # Map function names to their source location for function where inlining happened + self.caller_loc = caller_loc + +context = Context() + +class SourceFileRenderer: + def __init__(self, source_dir, output_dir, 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.stream = open(os.path.join(output_dir, optrecord.html_file_name(filename)), 'w') + if existing_filename: + self.source_stream = open(existing_filename) + else: + self.source_stream = None + print(''' + +

Unable to locate file {}

+ + '''.format(filename), file=self.stream) + + self.html_formatter = HtmlFormatter(encoding='utf-8') + self.cpp_lexer = CppLexer(stripnl=False) + + def render_source_lines(self, stream, line_remarks): + file_text = stream.read() + html_highlighted = highlight(file_text, self.cpp_lexer, self.html_formatter) + + # 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(''' + +{linenum} + + +
{html_line}
+'''.format(**locals()), file=self.stream) + + for remark in line_remarks.get(linenum, []): + 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) + if dl: + link = optrecord.make_link(dl['File'], dl['Line'] - 2) + inlining_context = "{r.DemangledFunctionName}".format(**locals()) + + # 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) + + print(''' + + +{r.RelativeHotness} +{r.PassWithDiffPrefix} +
{indent}
{r.message}  +{inlining_context} +'''.format(**locals()), file=self.stream) + + def render(self, line_remarks): + if not self.source_stream: + return + + print(''' + + + + + +
+ + + + + + + +''', file=self.stream) + self.render_source_lines(self.source_stream, line_remarks) + + print(''' +
LineHotnessOptimizationSourceInline Context
+ +''', file=self.stream) + + +class IndexRenderer: + def __init__(self, output_dir): + self.stream = open(os.path.join(output_dir, 'index.html'), 'w') + + def render_entry(self, r, odd): + escaped_name = cgi.escape(r.DemangledFunctionName) + print(''' + +{r.DebugLocString} +{r.RelativeHotness} +{escaped_name} +{r.PassWithDiffPrefix} +'''.format(**locals()), file=self.stream) + + def render(self, all_remarks): + print(''' + + + + + +
+ + + + + + +''', file=self.stream) + for i, remark in enumerate(all_remarks): + self.render_entry(remark, i % 2) + print(''' +
Source LocationHotnessFunctionPass
+ +''', file=self.stream) + + +def _render_file(source_dir, output_dir, ctx, entry): + global context + context = ctx + filename, remarks = entry + SourceFileRenderer(source_dir, output_dir, filename).render(remarks) + + +def map_remarks(all_remarks): + # Set up a map between function names and their source location for + # function where inlining happened + for remark in optrecord.itervalues(all_remarks): + if isinstance(remark, optrecord.Passed) and remark.Pass == "inline" and remark.Name == "Inlined": + for arg in remark.Args: + caller = arg.get('Caller') + if caller: + context.caller_loc[caller] = arg['DebugLoc'] + + +def generate_report(all_remarks, + file_remarks, + source_dir, + output_dir, + should_display_hotness, + num_jobs, + should_print_progress): + try: + os.makedirs(output_dir) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(output_dir): + pass + else: + raise + + _render_file_bound = functools.partial(_render_file, source_dir, output_dir, context) + if should_print_progress: + print('Rendering HTML files...') + optpmap.pmap(_render_file_bound, + file_remarks.items(), + num_jobs, + should_print_progress) + + if should_display_hotness: + sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.Hotness, r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function), reverse=True) + else: + sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function)) + IndexRenderer(args.output_dir).render(sorted_remarks) + + shutil.copy(os.path.join(os.path.dirname(os.path.realpath(__file__)), + "style.css"), output_dir) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=desc) + parser.add_argument('yaml_files', nargs='+') + parser.add_argument( + '--output-dir', + '-o', + default='html', + help='Path to a directory where generated HTML files will be output. ' + 'If the directory does not already exist, it will be created. ' + '"%(default)s" by default.') + parser.add_argument( + '--jobs', + '-j', + default=cpu_count(), + type=int, + help='Max job count (defaults to %(default)s, the current CPU count)') + parser.add_argument( + '-source-dir', + '-s', + default='', + help='set source directory') + parser.add_argument( + '--no-progress-indicator', + '-n', + action='store_true', + default=False, + help='Do not display any indicator of how many YAML files were read ' + 'or rendered into HTML.') + args = parser.parse_args() + + print_progress = not args.no_progress_indicator + all_remarks, file_remarks, should_display_hotness = \ + optrecord.gather_results(args.yaml_files, args.jobs, print_progress) + + map_remarks(all_remarks) + + generate_report(all_remarks, + file_remarks, + args.source_dir, + args.output_dir, + should_display_hotness, + args.jobs, + print_progress) Index: llvm/trunk/tools/opt-viewer/optpmap.py =================================================================== --- llvm/trunk/tools/opt-viewer/optpmap.py +++ llvm/trunk/tools/opt-viewer/optpmap.py @@ -0,0 +1,53 @@ +import sys +import multiprocessing + + +_current = None +_total = None + + +def _init(current, total): + global _current + global _total + _current = current + _total = total + + +def _wrapped_func(func_and_args): + func, argument, should_print_progress = func_and_args + + if should_print_progress: + with _current.get_lock(): + _current.value += 1 + sys.stdout.write('\r\t{} of {}'.format(_current.value, _total.value)) + + return func(argument) + + +def pmap(func, iterable, processes, should_print_progress, *args, **kwargs): + """ + A parallel map function that reports on its progress. + + Applies `func` to every item of `iterable` and return a list of the + results. If `processes` is greater than one, a process pool is used to run + the functions in parallel. `should_print_progress` is a boolean value that + indicates whether a string 'N of M' should be printed to indicate how many + of the functions have finished being run. + """ + global _current + global _total + _current = multiprocessing.Value('i', 0) + _total = multiprocessing.Value('i', len(iterable)) + + func_and_args = [(func, arg, should_print_progress,) for arg in iterable] + if processes <= 1: + result = map(_wrapped_func, func_and_args, *args, **kwargs) + else: + pool = multiprocessing.Pool(initializer=_init, + initargs=(_current, _total,), + processes=processes) + result = pool.map(_wrapped_func, func_and_args, *args, **kwargs) + + if should_print_progress: + sys.stdout.write('\r') + return result Index: llvm/trunk/tools/opt-viewer/optrecord.py =================================================================== --- llvm/trunk/tools/opt-viewer/optrecord.py +++ llvm/trunk/tools/opt-viewer/optrecord.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python2.7 + +from __future__ import print_function + +import yaml +# Try to use the C parser. +try: + from yaml import CLoader as Loader +except ImportError: + print("For faster parsing, you may want to install libYAML for PyYAML") + from yaml import Loader + +import cgi +from collections import defaultdict +import functools +from multiprocessing import Lock +import subprocess + +import optpmap + + +p = subprocess.Popen(['c++filt', '-n'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) +p_lock = Lock() + + +try: + dict.iteritems +except AttributeError: + # Python 3 + def itervalues(d): + return iter(d.values()) + def iteritems(d): + return iter(d.items()) +else: + # Python 2 + def itervalues(d): + return d.itervalues() + def iteritems(d): + return d.iteritems() + + +def demangle(name): + with p_lock: + p.stdin.write((name + '\n').encode('utf-8')) + p.stdin.flush() + return p.stdout.readline().rstrip().decode('utf-8') + + +def html_file_name(filename): + return filename.replace('/', '_') + ".html" + + +def make_link(File, Line): + return "\"{}#L{}\"".format(html_file_name(File), Line) + + +class Remark(yaml.YAMLObject): + # Work-around for http://pyyaml.org/ticket/154. + yaml_loader = Loader + + def initmissing(self): + if not hasattr(self, 'Hotness'): + self.Hotness = 0 + if not hasattr(self, 'Args'): + self.Args = [] + + @property + def File(self): + return self.DebugLoc['File'] + + @property + def Line(self): + return int(self.DebugLoc['Line']) + + @property + def Column(self): + return self.DebugLoc['Column'] + + @property + def DebugLocString(self): + return "{}:{}:{}".format(self.File, self.Line, self.Column) + + @property + def DemangledFunctionName(self): + return demangle(self.Function) + + @property + def Link(self): + return make_link(self.File, self.Line) + + def getArgString(self, mapping): + mapping = mapping.copy() + dl = mapping.get('DebugLoc') + if dl: + del mapping['DebugLoc'] + + assert(len(mapping) == 1) + (key, value) = mapping.items()[0] + + if key == 'Caller' or key == 'Callee': + value = cgi.escape(demangle(value)) + + if dl and key != 'Caller': + return "{}".format( + make_link(dl['File'], dl['Line']), value) + else: + return value + + def getDiffPrefix(self): + if hasattr(self, 'Added'): + if self.Added: + return '+' + else: + return '-' + return '' + + @property + def PassWithDiffPrefix(self): + return self.getDiffPrefix() + self.Pass + + @property + def message(self): + # Args is a list of mappings (dictionaries) + values = [self.getArgString(mapping) for mapping in self.Args] + return "".join(values) + + @property + def RelativeHotness(self): + if self.max_hotness: + return "{}%".format(int(round(self.Hotness * 100 / self.max_hotness))) + else: + return '' + + @property + def key(self): + k = (self.__class__, self.PassWithDiffPrefix, self.Name, self.File, self.Line, self.Column, self.Function) + for arg in self.Args: + for (key, value) in iteritems(arg): + if type(value) is dict: + value = tuple(value.items()) + k += (key, value) + return k + + def __hash__(self): + return hash(self.key) + + def __eq__(self, other): + return self.key == other.key + + def __repr__(self): + return str(self.key) + + +class Analysis(Remark): + yaml_tag = '!Analysis' + + @property + def color(self): + return "white" + + +class AnalysisFPCommute(Analysis): + yaml_tag = '!AnalysisFPCommute' + + +class AnalysisAliasing(Analysis): + yaml_tag = '!AnalysisAliasing' + + +class Passed(Remark): + yaml_tag = '!Passed' + + @property + def color(self): + return "green" + + +class Missed(Remark): + yaml_tag = '!Missed' + + @property + def color(self): + return "red" + + +def get_remarks(input_file): + max_hotness = 0 + all_remarks = dict() + file_remarks = defaultdict(functools.partial(defaultdict, list)) + + with open(input_file) as f: + docs = yaml.load_all(f, Loader=Loader) + for remark in docs: + remark.initmissing() + # Avoid remarks withoug debug location or if they are duplicated + if not hasattr(remark, 'DebugLoc') or remark.key in all_remarks: + continue + all_remarks[remark.key] = remark + + file_remarks[remark.File][remark.Line].append(remark) + + # If we're reading a back a diff yaml file, max_hotness is already + # captured which may actually be less than the max hotness found + # in the file. + if hasattr(remark, 'max_hotness'): + max_hotness = remark.max_hotness + max_hotness = max(max_hotness, remark.Hotness) + + return max_hotness, all_remarks, file_remarks + + +def gather_results(filenames, num_jobs, should_print_progress): + if should_print_progress: + print('Reading YAML files...') + remarks = optpmap.pmap( + get_remarks, filenames, num_jobs, should_print_progress) + max_hotness = max(entry[0] for entry in remarks) + + def merge_file_remarks(file_remarks_job, all_remarks, merged): + for filename, d in iteritems(file_remarks_job): + for line, remarks in iteritems(d): + for remark in remarks: + # Bring max_hotness into the remarks so that + # RelativeHotness does not depend on an external global. + remark.max_hotness = max_hotness + if remark.key not in all_remarks: + merged[filename][line].append(remark) + + all_remarks = dict() + file_remarks = defaultdict(functools.partial(defaultdict, list)) + for _, all_remarks_job, file_remarks_job in remarks: + merge_file_remarks(file_remarks_job, all_remarks, file_remarks) + all_remarks.update(all_remarks_job) + + return all_remarks, file_remarks, max_hotness != 0 Index: llvm/trunk/tools/opt-viewer/style.css =================================================================== --- llvm/trunk/tools/opt-viewer/style.css +++ llvm/trunk/tools/opt-viewer/style.css @@ -0,0 +1,198 @@ +.red { + background-color: #ffd0d0; +} +.cyan { + background-color: cyan; +} +body { + font-family: -apple-system, sans-serif; +} +pre { + margin-top: 0px !important; + margin-bottom: 0px !important; +} +.source-name-title { + padding: 5px 10px; + border-bottom: 1px solid #dbdbdb; + background-color: #eee; + line-height: 35px; +} +.centered { + display: table; + margin-left: left; + margin-right: auto; + border: 1px solid #dbdbdb; + border-radius: 3px; +} +.expansion-view { + background-color: rgba(0, 0, 0, 0); + margin-left: 0px; + margin-top: 5px; + margin-right: 5px; + margin-bottom: 5px; + border: 1px solid #dbdbdb; + border-radius: 3px; +} +table { + border-collapse: collapse; +} +.light-row { + background: #ffffff; + border: 1px solid #dbdbdb; +} +.column-entry { + text-align: right; +} +.column-entry-left { + text-align: left; +} +.column-entry-white { + text-align: right; + background-color: #ffffff; +} +.column-entry-red { + text-align: right; + background-color: #ffd0d0; +} +.column-entry-green { + text-align: right; + background-color: #d0ffd0; +} +.column-entry-yellow { + text-align: left; + background-color: #ffe1a6; +} +.column-entry-0 { + background-color: #ffffff; +} +.column-entry-1 { + background-color: #eeeeee; +} +.line-number { + text-align: right; + color: #aaa; +} +.covered-line { + text-align: right; + color: #0080ff; +} +.uncovered-line { + text-align: right; + color: #ff3300; +} +.tooltip { + position: relative; + display: inline; + background-color: #b3e6ff; + text-decoration: none; +} +.tooltip span.tooltip-content { + position: absolute; + width: 100px; + margin-left: -50px; + color: #FFFFFF; + background: #000000; + height: 30px; + line-height: 30px; + text-align: center; + visibility: hidden; + border-radius: 6px; +} +.tooltip span.tooltip-content:after { + content: ''; + position: absolute; + top: 100%; + left: 50%; + margin-left: -8px; + width: 0; height: 0; + border-top: 8px solid #000000; + border-right: 8px solid transparent; + border-left: 8px solid transparent; +} +:hover.tooltip span.tooltip-content { + visibility: visible; + opacity: 0.8; + bottom: 30px; + left: 50%; + z-index: 999; +} +th, td { + vertical-align: top; + padding: 2px 5px; + border-collapse: collapse; + border-right: solid 1px #eee; + border-left: solid 1px #eee; +} +td:first-child { + border-left: none; +} +td:last-child { + border-right: none; +} + +/* Generated with pygmentize -S colorful -f html >> 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 */ Index: llvm/trunk/utils/opt-viewer/opt-diff.py =================================================================== --- llvm/trunk/utils/opt-viewer/opt-diff.py +++ llvm/trunk/utils/opt-viewer/opt-diff.py @@ -1,71 +0,0 @@ -#!/usr/bin/env python2.7 - -from __future__ import print_function - -desc = '''Generate the difference of two YAML files into a new YAML file (works on -pair of directories too). A new attribute 'Added' is set to True or False -depending whether the entry is added or removed from the first input to the -next. - -The tools requires PyYAML.''' - -import yaml -# Try to use the C parser. -try: - from yaml import CLoader as Loader -except ImportError: - from yaml import Loader - -import optrecord -import argparse -from collections import defaultdict -from multiprocessing import cpu_count, Pool -import os, os.path -import fnmatch - -def find_files(dir_or_file): - if os.path.isfile(dir_or_file): - return [dir_or_file] - - all = [] - for dir, subdirs, files in os.walk(dir_or_file): - for file in files: - if fnmatch.fnmatch(file, "*.opt.yaml"): - all.append( os.path.join(dir, file)) - return all - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description=desc) - parser.add_argument('yaml_dir_or_file_1') - parser.add_argument('yaml_dir_or_file_2') - parser.add_argument( - '--jobs', - '-j', - default=cpu_count(), - type=int, - help='Max job count (defaults to %(default)s, the current CPU count)') - parser.add_argument( - '--no-progress-indicator', - '-n', - action='store_true', - default=False, - help='Do not display any indicator of how many YAML files were read.') - parser.add_argument('--output', '-o', default='diff.opt.yaml') - args = parser.parse_args() - - files1 = find_files(args.yaml_dir_or_file_1) - files2 = find_files(args.yaml_dir_or_file_2) - - print_progress = not args.no_progress_indicator - all_remarks1, _, _ = optrecord.gather_results(files1, args.jobs, print_progress) - all_remarks2, _, _ = optrecord.gather_results(files2, args.jobs, print_progress) - - added = set(all_remarks2.values()) - set(all_remarks1.values()) - removed = set(all_remarks1.values()) - set(all_remarks2.values()) - - for r in added: - r.Added = True - for r in removed: - r.Added = False - with open(args.output, 'w') as stream: - yaml.dump_all(added | removed, stream) Index: llvm/trunk/utils/opt-viewer/opt-stats.py =================================================================== --- llvm/trunk/utils/opt-viewer/opt-stats.py +++ llvm/trunk/utils/opt-viewer/opt-stats.py @@ -1,56 +0,0 @@ -#!/usr/bin/env python2.7 - -from __future__ import print_function - -desc = '''Generate statistics about optimization records from the YAML files -generated with -fsave-optimization-record and -fdiagnostics-show-hotness. - -The tools requires PyYAML and Pygments Python packages.''' - -import optrecord -import argparse -import operator -from collections import defaultdict -from multiprocessing import cpu_count, Pool - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description=desc) - parser.add_argument('yaml_files', nargs='+') - parser.add_argument( - '--jobs', - '-j', - default=cpu_count(), - type=int, - help='Max job count (defaults to %(default)s, the current CPU count)') - parser.add_argument( - '--no-progress-indicator', - '-n', - action='store_true', - default=False, - help='Do not display any indicator of how many YAML files were read.') - args = parser.parse_args() - - print_progress = not args.no_progress_indicator - all_remarks, file_remarks, _ = optrecord.gather_results( - args.yaml_files, args.jobs, print_progress) - if print_progress: - print('\n') - - bypass = defaultdict(int) - byname = defaultdict(int) - for r in optrecord.itervalues(all_remarks): - bypass[r.Pass] += 1 - byname[r.Pass + "/" + r.Name] += 1 - - total = len(all_remarks) - print("{:24s} {:10d}\n".format("Total number of remarks", total)) - - print("Top 10 remarks by pass:") - for (passname, count) in sorted(bypass.items(), key=operator.itemgetter(1), - reverse=True)[:10]: - print(" {:30s} {:2.0f}%". format(passname, count * 100. / total)) - - print("\nTop 10 remarks:") - for (name, count) in sorted(byname.items(), key=operator.itemgetter(1), - reverse=True)[:10]: - print(" {:30s} {:2.0f}%". format(name, count * 100. / total)) Index: llvm/trunk/utils/opt-viewer/opt-viewer.py =================================================================== --- llvm/trunk/utils/opt-viewer/opt-viewer.py +++ llvm/trunk/utils/opt-viewer/opt-viewer.py @@ -1,259 +0,0 @@ -#!/usr/bin/env python2.7 - -from __future__ import print_function - -import argparse -import cgi -import errno -import functools -from multiprocessing import cpu_count -import os.path -import re -import shutil - -from pygments import highlight -from pygments.lexers.c_cpp import CppLexer -from pygments.formatters import HtmlFormatter - -import optpmap -import optrecord - - -desc = '''Generate HTML output to visualize optimization records from the YAML files -generated with -fsave-optimization-record and -fdiagnostics-show-hotness. - -The tools requires PyYAML and Pygments Python packages.''' - - -# This allows passing the global context to the child processes. -class Context: - def __init__(self, caller_loc = dict()): - # Map function names to their source location for function where inlining happened - self.caller_loc = caller_loc - -context = Context() - -class SourceFileRenderer: - def __init__(self, source_dir, output_dir, 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.stream = open(os.path.join(output_dir, optrecord.html_file_name(filename)), 'w') - if existing_filename: - self.source_stream = open(existing_filename) - else: - self.source_stream = None - print(''' - -

Unable to locate file {}

- - '''.format(filename), file=self.stream) - - self.html_formatter = HtmlFormatter(encoding='utf-8') - self.cpp_lexer = CppLexer(stripnl=False) - - def render_source_lines(self, stream, line_remarks): - file_text = stream.read() - html_highlighted = highlight(file_text, self.cpp_lexer, self.html_formatter) - - # 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(''' - -{linenum} - - -
{html_line}
-'''.format(**locals()), file=self.stream) - - for remark in line_remarks.get(linenum, []): - 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) - if dl: - link = optrecord.make_link(dl['File'], dl['Line'] - 2) - inlining_context = "{r.DemangledFunctionName}".format(**locals()) - - # 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) - - print(''' - - -{r.RelativeHotness} -{r.PassWithDiffPrefix} -
{indent}
{r.message}  -{inlining_context} -'''.format(**locals()), file=self.stream) - - def render(self, line_remarks): - if not self.source_stream: - return - - print(''' - - - - - -
- - - - - - - -''', file=self.stream) - self.render_source_lines(self.source_stream, line_remarks) - - print(''' -
LineHotnessOptimizationSourceInline Context
- -''', file=self.stream) - - -class IndexRenderer: - def __init__(self, output_dir): - self.stream = open(os.path.join(output_dir, 'index.html'), 'w') - - def render_entry(self, r, odd): - escaped_name = cgi.escape(r.DemangledFunctionName) - print(''' - -{r.DebugLocString} -{r.RelativeHotness} -{escaped_name} -{r.PassWithDiffPrefix} -'''.format(**locals()), file=self.stream) - - def render(self, all_remarks): - print(''' - - - - - -
- - - - - - -''', file=self.stream) - for i, remark in enumerate(all_remarks): - self.render_entry(remark, i % 2) - print(''' -
Source LocationHotnessFunctionPass
- -''', file=self.stream) - - -def _render_file(source_dir, output_dir, ctx, entry): - global context - context = ctx - filename, remarks = entry - SourceFileRenderer(source_dir, output_dir, filename).render(remarks) - - -def map_remarks(all_remarks): - # Set up a map between function names and their source location for - # function where inlining happened - for remark in optrecord.itervalues(all_remarks): - if isinstance(remark, optrecord.Passed) and remark.Pass == "inline" and remark.Name == "Inlined": - for arg in remark.Args: - caller = arg.get('Caller') - if caller: - context.caller_loc[caller] = arg['DebugLoc'] - - -def generate_report(all_remarks, - file_remarks, - source_dir, - output_dir, - should_display_hotness, - num_jobs, - should_print_progress): - try: - os.makedirs(output_dir) - except OSError as e: - if e.errno == errno.EEXIST and os.path.isdir(output_dir): - pass - else: - raise - - _render_file_bound = functools.partial(_render_file, source_dir, output_dir, context) - if should_print_progress: - print('Rendering HTML files...') - optpmap.pmap(_render_file_bound, - file_remarks.items(), - num_jobs, - should_print_progress) - - if should_display_hotness: - sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.Hotness, r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function), reverse=True) - else: - sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function)) - IndexRenderer(args.output_dir).render(sorted_remarks) - - shutil.copy(os.path.join(os.path.dirname(os.path.realpath(__file__)), - "style.css"), output_dir) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description=desc) - parser.add_argument('yaml_files', nargs='+') - parser.add_argument( - '--output-dir', - '-o', - default='html', - help='Path to a directory where generated HTML files will be output. ' - 'If the directory does not already exist, it will be created. ' - '"%(default)s" by default.') - parser.add_argument( - '--jobs', - '-j', - default=cpu_count(), - type=int, - help='Max job count (defaults to %(default)s, the current CPU count)') - parser.add_argument( - '-source-dir', - '-s', - default='', - help='set source directory') - parser.add_argument( - '--no-progress-indicator', - '-n', - action='store_true', - default=False, - help='Do not display any indicator of how many YAML files were read ' - 'or rendered into HTML.') - args = parser.parse_args() - - print_progress = not args.no_progress_indicator - all_remarks, file_remarks, should_display_hotness = \ - optrecord.gather_results(args.yaml_files, args.jobs, print_progress) - - map_remarks(all_remarks) - - generate_report(all_remarks, - file_remarks, - args.source_dir, - args.output_dir, - should_display_hotness, - args.jobs, - print_progress) Index: llvm/trunk/utils/opt-viewer/optpmap.py =================================================================== --- llvm/trunk/utils/opt-viewer/optpmap.py +++ llvm/trunk/utils/opt-viewer/optpmap.py @@ -1,53 +0,0 @@ -import sys -import multiprocessing - - -_current = None -_total = None - - -def _init(current, total): - global _current - global _total - _current = current - _total = total - - -def _wrapped_func(func_and_args): - func, argument, should_print_progress = func_and_args - - if should_print_progress: - with _current.get_lock(): - _current.value += 1 - sys.stdout.write('\r\t{} of {}'.format(_current.value, _total.value)) - - return func(argument) - - -def pmap(func, iterable, processes, should_print_progress, *args, **kwargs): - """ - A parallel map function that reports on its progress. - - Applies `func` to every item of `iterable` and return a list of the - results. If `processes` is greater than one, a process pool is used to run - the functions in parallel. `should_print_progress` is a boolean value that - indicates whether a string 'N of M' should be printed to indicate how many - of the functions have finished being run. - """ - global _current - global _total - _current = multiprocessing.Value('i', 0) - _total = multiprocessing.Value('i', len(iterable)) - - func_and_args = [(func, arg, should_print_progress,) for arg in iterable] - if processes <= 1: - result = map(_wrapped_func, func_and_args, *args, **kwargs) - else: - pool = multiprocessing.Pool(initializer=_init, - initargs=(_current, _total,), - processes=processes) - result = pool.map(_wrapped_func, func_and_args, *args, **kwargs) - - if should_print_progress: - sys.stdout.write('\r') - return result Index: llvm/trunk/utils/opt-viewer/optrecord.py =================================================================== --- llvm/trunk/utils/opt-viewer/optrecord.py +++ llvm/trunk/utils/opt-viewer/optrecord.py @@ -1,235 +0,0 @@ -#!/usr/bin/env python2.7 - -from __future__ import print_function - -import yaml -# Try to use the C parser. -try: - from yaml import CLoader as Loader -except ImportError: - print("For faster parsing, you may want to install libYAML for PyYAML") - from yaml import Loader - -import cgi -from collections import defaultdict -import functools -from multiprocessing import Lock -import subprocess - -import optpmap - - -p = subprocess.Popen(['c++filt', '-n'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) -p_lock = Lock() - - -try: - dict.iteritems -except AttributeError: - # Python 3 - def itervalues(d): - return iter(d.values()) - def iteritems(d): - return iter(d.items()) -else: - # Python 2 - def itervalues(d): - return d.itervalues() - def iteritems(d): - return d.iteritems() - - -def demangle(name): - with p_lock: - p.stdin.write((name + '\n').encode('utf-8')) - p.stdin.flush() - return p.stdout.readline().rstrip().decode('utf-8') - - -def html_file_name(filename): - return filename.replace('/', '_') + ".html" - - -def make_link(File, Line): - return "\"{}#L{}\"".format(html_file_name(File), Line) - - -class Remark(yaml.YAMLObject): - # Work-around for http://pyyaml.org/ticket/154. - yaml_loader = Loader - - def initmissing(self): - if not hasattr(self, 'Hotness'): - self.Hotness = 0 - if not hasattr(self, 'Args'): - self.Args = [] - - @property - def File(self): - return self.DebugLoc['File'] - - @property - def Line(self): - return int(self.DebugLoc['Line']) - - @property - def Column(self): - return self.DebugLoc['Column'] - - @property - def DebugLocString(self): - return "{}:{}:{}".format(self.File, self.Line, self.Column) - - @property - def DemangledFunctionName(self): - return demangle(self.Function) - - @property - def Link(self): - return make_link(self.File, self.Line) - - def getArgString(self, mapping): - mapping = mapping.copy() - dl = mapping.get('DebugLoc') - if dl: - del mapping['DebugLoc'] - - assert(len(mapping) == 1) - (key, value) = mapping.items()[0] - - if key == 'Caller' or key == 'Callee': - value = cgi.escape(demangle(value)) - - if dl and key != 'Caller': - return "{}".format( - make_link(dl['File'], dl['Line']), value) - else: - return value - - def getDiffPrefix(self): - if hasattr(self, 'Added'): - if self.Added: - return '+' - else: - return '-' - return '' - - @property - def PassWithDiffPrefix(self): - return self.getDiffPrefix() + self.Pass - - @property - def message(self): - # Args is a list of mappings (dictionaries) - values = [self.getArgString(mapping) for mapping in self.Args] - return "".join(values) - - @property - def RelativeHotness(self): - if self.max_hotness: - return "{}%".format(int(round(self.Hotness * 100 / self.max_hotness))) - else: - return '' - - @property - def key(self): - k = (self.__class__, self.PassWithDiffPrefix, self.Name, self.File, self.Line, self.Column, self.Function) - for arg in self.Args: - for (key, value) in iteritems(arg): - if type(value) is dict: - value = tuple(value.items()) - k += (key, value) - return k - - def __hash__(self): - return hash(self.key) - - def __eq__(self, other): - return self.key == other.key - - def __repr__(self): - return str(self.key) - - -class Analysis(Remark): - yaml_tag = '!Analysis' - - @property - def color(self): - return "white" - - -class AnalysisFPCommute(Analysis): - yaml_tag = '!AnalysisFPCommute' - - -class AnalysisAliasing(Analysis): - yaml_tag = '!AnalysisAliasing' - - -class Passed(Remark): - yaml_tag = '!Passed' - - @property - def color(self): - return "green" - - -class Missed(Remark): - yaml_tag = '!Missed' - - @property - def color(self): - return "red" - - -def get_remarks(input_file): - max_hotness = 0 - all_remarks = dict() - file_remarks = defaultdict(functools.partial(defaultdict, list)) - - with open(input_file) as f: - docs = yaml.load_all(f, Loader=Loader) - for remark in docs: - remark.initmissing() - # Avoid remarks withoug debug location or if they are duplicated - if not hasattr(remark, 'DebugLoc') or remark.key in all_remarks: - continue - all_remarks[remark.key] = remark - - file_remarks[remark.File][remark.Line].append(remark) - - # If we're reading a back a diff yaml file, max_hotness is already - # captured which may actually be less than the max hotness found - # in the file. - if hasattr(remark, 'max_hotness'): - max_hotness = remark.max_hotness - max_hotness = max(max_hotness, remark.Hotness) - - return max_hotness, all_remarks, file_remarks - - -def gather_results(filenames, num_jobs, should_print_progress): - if should_print_progress: - print('Reading YAML files...') - remarks = optpmap.pmap( - get_remarks, filenames, num_jobs, should_print_progress) - max_hotness = max(entry[0] for entry in remarks) - - def merge_file_remarks(file_remarks_job, all_remarks, merged): - for filename, d in iteritems(file_remarks_job): - for line, remarks in iteritems(d): - for remark in remarks: - # Bring max_hotness into the remarks so that - # RelativeHotness does not depend on an external global. - remark.max_hotness = max_hotness - if remark.key not in all_remarks: - merged[filename][line].append(remark) - - all_remarks = dict() - file_remarks = defaultdict(functools.partial(defaultdict, list)) - for _, all_remarks_job, file_remarks_job in remarks: - merge_file_remarks(file_remarks_job, all_remarks, file_remarks) - all_remarks.update(all_remarks_job) - - return all_remarks, file_remarks, max_hotness != 0 Index: llvm/trunk/utils/opt-viewer/style.css =================================================================== --- llvm/trunk/utils/opt-viewer/style.css +++ llvm/trunk/utils/opt-viewer/style.css @@ -1,198 +0,0 @@ -.red { - background-color: #ffd0d0; -} -.cyan { - background-color: cyan; -} -body { - font-family: -apple-system, sans-serif; -} -pre { - margin-top: 0px !important; - margin-bottom: 0px !important; -} -.source-name-title { - padding: 5px 10px; - border-bottom: 1px solid #dbdbdb; - background-color: #eee; - line-height: 35px; -} -.centered { - display: table; - margin-left: left; - margin-right: auto; - border: 1px solid #dbdbdb; - border-radius: 3px; -} -.expansion-view { - background-color: rgba(0, 0, 0, 0); - margin-left: 0px; - margin-top: 5px; - margin-right: 5px; - margin-bottom: 5px; - border: 1px solid #dbdbdb; - border-radius: 3px; -} -table { - border-collapse: collapse; -} -.light-row { - background: #ffffff; - border: 1px solid #dbdbdb; -} -.column-entry { - text-align: right; -} -.column-entry-left { - text-align: left; -} -.column-entry-white { - text-align: right; - background-color: #ffffff; -} -.column-entry-red { - text-align: right; - background-color: #ffd0d0; -} -.column-entry-green { - text-align: right; - background-color: #d0ffd0; -} -.column-entry-yellow { - text-align: left; - background-color: #ffe1a6; -} -.column-entry-0 { - background-color: #ffffff; -} -.column-entry-1 { - background-color: #eeeeee; -} -.line-number { - text-align: right; - color: #aaa; -} -.covered-line { - text-align: right; - color: #0080ff; -} -.uncovered-line { - text-align: right; - color: #ff3300; -} -.tooltip { - position: relative; - display: inline; - background-color: #b3e6ff; - text-decoration: none; -} -.tooltip span.tooltip-content { - position: absolute; - width: 100px; - margin-left: -50px; - color: #FFFFFF; - background: #000000; - height: 30px; - line-height: 30px; - text-align: center; - visibility: hidden; - border-radius: 6px; -} -.tooltip span.tooltip-content:after { - content: ''; - position: absolute; - top: 100%; - left: 50%; - margin-left: -8px; - width: 0; height: 0; - border-top: 8px solid #000000; - border-right: 8px solid transparent; - border-left: 8px solid transparent; -} -:hover.tooltip span.tooltip-content { - visibility: visible; - opacity: 0.8; - bottom: 30px; - left: 50%; - z-index: 999; -} -th, td { - vertical-align: top; - padding: 2px 5px; - border-collapse: collapse; - border-right: solid 1px #eee; - border-left: solid 1px #eee; -} -td:first-child { - border-left: none; -} -td:last-child { - border-right: none; -} - -/* Generated with pygmentize -S colorful -f html >> 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 */