Index: cmake/modules/AddLLVM.cmake
===================================================================
--- cmake/modules/AddLLVM.cmake
+++ cmake/modules/AddLLVM.cmake
@@ -1635,3 +1635,34 @@
)
endif()
endmacro()
+
+function(find_python_module module)
+ string(TOUPPER ${module} module_upper)
+ set(FOUND_VAR PY_${module_upper}_FOUND)
+
+ execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" "import ${module}"
+ RESULT_VARIABLE status
+ ERROR_QUIET)
+
+ if(status)
+ set(${FOUND_VAR} 0 PARENT_SCOPE)
+ message(STATUS "Could NOT find Python module ${module}")
+ else()
+ set(${FOUND_VAR} 1 PARENT_SCOPE)
+ message(STATUS "Found Python module ${module}")
+ endif()
+endfunction()
+
+set (PYTHON_MODULES
+ pygments
+ yaml
+ )
+foreach(module ${PYTHON_MODULES})
+ find_python_module(${module})
+endforeach()
+
+if(PY_PYGMENTS_FOUND AND PY_YAML_FOUND)
+ set (LLVM_HAVE_OPT_VIEWER_MODULES 1)
+else()
+ set (LLVM_HAVE_OPT_VIEWER_MODULES 0)
+endif()
Index: test/lit.cfg.py
===================================================================
--- test/lit.cfg.py
+++ test/lit.cfg.py
@@ -123,6 +123,7 @@
ocamlopt_command = '%s ocamlopt -cclib -L%s -cclib -Wl,-rpath,%s %s' % (
config.ocamlfind_executable, config.llvm_lib_dir, config.llvm_lib_dir, config.ocaml_flags)
+opt_viewer_cmd = '%s %s/tools/opt-viewer/opt-viewer.py' % (sys.executable, config.llvm_src_root)
tools = [
ToolSubst('%lli', FindTool('lli'), post='.', extra_args=lli_args),
@@ -132,6 +133,7 @@
ToolSubst('%ld64', ld64_cmd, unresolved='ignore'),
ToolSubst('%ocamlc', ocamlc_command, unresolved='ignore'),
ToolSubst('%ocamlopt', ocamlopt_command, unresolved='ignore'),
+ ToolSubst('%opt-viewer', opt_viewer_cmd),
]
# FIXME: Why do we have both `lli` and `%lli` that do slightly different things?
@@ -286,3 +288,6 @@
if config.llvm_libxml2_enabled == '1':
config.available_features.add('libxml2')
+
+if config.have_opt_viewer_modules:
+ config.available_features.add('have_opt_viewer_modules')
Index: test/lit.site.cfg.py.in
===================================================================
--- test/lit.site.cfg.py.in
+++ test/lit.site.cfg.py.in
@@ -43,6 +43,7 @@
config.llvm_libxml2_enabled = "@LLVM_LIBXML2_ENABLED@"
config.llvm_host_triple = '@LLVM_HOST_TRIPLE@'
config.host_arch = "@HOST_ARCH@"
+config.have_opt_viewer_modules = @LLVM_HAVE_OPT_VIEWER_MODULES@
# Support substitution of the tools_dir with user parameters. This is
# used when we can't determine the tool dir at configuration time.
Index: test/tools/opt-viewer/Inputs/basic/or.h
===================================================================
--- /dev/null
+++ test/tools/opt-viewer/Inputs/basic/or.h
@@ -0,0 +1,16 @@
+void TestH(int *res, int *c, int *d, int *p, int n) {
+ int i;
+
+#pragma clang loop vectorize(assume_safety)
+ for (i = 0; i < 1600; i++) {
+ res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
+ }
+
+ for (i = 0; i < 16; i++) {
+ res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
+ }
+
+ foo();
+
+ foo(); bar(); foo();
+}
Index: test/tools/opt-viewer/Inputs/basic/or.c
===================================================================
--- /dev/null
+++ test/tools/opt-viewer/Inputs/basic/or.c
@@ -0,0 +1,21 @@
+void bar();
+void foo() { bar(); }
+
+#include "or.h"
+
+void Test(int *res, int *c, int *d, int *p, int n) {
+ int i;
+
+#pragma clang loop vectorize(assume_safety)
+ for (i = 0; i < 1600; i++) {
+ res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
+ }
+
+ for (i = 0; i < 16; i++) {
+ res[i] = (p[i] == 0) ? res[i] : res[i] + d[i];
+ }
+
+ foo();
+
+ foo(); bar(); foo();
+}
Index: test/tools/opt-viewer/Inputs/basic/or.yaml
===================================================================
--- /dev/null
+++ test/tools/opt-viewer/Inputs/basic/or.yaml
@@ -0,0 +1,227 @@
+--- !Missed
+Pass: inline
+Name: NoDefinition
+DebugLoc: { File: basic/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: basic/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: basic/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: basic/or.h, Line: 13, Column: 3 }
+Function: TestH
+Args:
+ - Callee: foo
+ - String: ' inlined into '
+ - Caller: TestH
+...
+--- !Analysis
+Pass: inline
+Name: CanBeInlined
+DebugLoc: { File: basic/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: basic/or.h, Line: 15, Column: 3 }
+Function: TestH
+Args:
+ - Callee: foo
+ - String: ' inlined into '
+ - Caller: TestH
+...
+--- !Analysis
+Pass: inline
+Name: CanBeInlined
+DebugLoc: { File: basic/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: basic/or.h, Line: 15, Column: 17 }
+Function: TestH
+Args:
+ - Callee: foo
+ - String: ' inlined into '
+ - Caller: TestH
+...
+--- !Passed
+Pass: loop-unroll
+Name: FullyUnrolled
+DebugLoc: { File: basic/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: basic/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: basic/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: basic/or.c, Line: 18, Column: 3 }
+Function: Test
+Args:
+ - Callee: foo
+ - String: ' inlined into '
+ - Caller: Test
+...
+--- !Analysis
+Pass: inline
+Name: CanBeInlined
+DebugLoc: { File: basic/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: basic/or.c, Line: 20, Column: 3 }
+Function: Test
+Args:
+ - Callee: foo
+ - String: ' inlined into '
+ - Caller: Test
+...
+--- !Analysis
+Pass: inline
+Name: CanBeInlined
+DebugLoc: { File: basic/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: basic/or.c, Line: 20, Column: 17 }
+Function: Test
+Args:
+ - Callee: foo
+ - String: ' inlined into '
+ - Caller: Test
+...
+--- !Passed
+Pass: loop-unroll
+Name: FullyUnrolled
+DebugLoc: { File: basic/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: basic/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: basic/or.c, Line: 10, Column: 3 }
+Function: Test
+Args:
+ - String: 'vectorized loop (vectorization width: '
+ - VectorizationFactor: '4'
+ - String: ', interleaved count: '
+ - InterleaveCount: '2'
+ - String: ')'
+...
Index: test/tools/opt-viewer/Outputs/basic/basic_or.h.html
===================================================================
--- /dev/null
+++ test/tools/opt-viewer/Outputs/basic/basic_or.h.html
@@ -0,0 +1,221 @@
+
+
+
+
+
+
+
+
+
+
+Line
+ | Hotness
+ | Optimization
+ | Source
+ | Inline Context
+ |
+
+
+
+
+1 |
+ |
+ |
+void TestH(int *res, int *c, int *d, int *p, int n) { |
+
+
+
+2 |
+ |
+ |
+ |
+
+
+
+3 |
+ |
+ |
+ |
+
+
+
+4 |
+ |
+ |
+#pragma clang loop vectorize(assume_safety) |
+
+
+
+5 |
+ |
+ |
+ for (i = 0; i < 1600; i++) { |
+
+
+
+ |
+ |
+loop-vectorize |
+ vectorized loop (vectorization width: 4, interleaved count: 2) |
+TestH |
+
+
+
+6 |
+ |
+ |
+ res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; |
+
+
+
+7 |
+ |
+ |
+ |
+
+
+
+8 |
+ |
+ |
+ |
+
+
+
+9 |
+ |
+ |
+ for (i = 0; i < 16; i++) { |
+
+
+
+ |
+ |
+loop-unroll |
+ completely unrolled loop with 16 iterations |
+TestH |
+
+
+
+10 |
+ |
+ |
+ res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; |
+
+
+
+11 |
+ |
+ |
+ |
+
+
+
+12 |
+ |
+ |
+ |
+
+
+
+13 |
+ |
+ |
+ |
+
+
+
+ |
+ |
+inline |
+ foo can be inlined into TestH with cost=30 (threshold=412) |
+TestH |
+
+
+
+ |
+ |
+inline |
+ foo inlined into TestH |
+TestH |
+
+
+
+14 |
+ |
+ |
+ |
+
+
+
+15 |
+ |
+ |
+ |
+
+
+
+ |
+ |
+inline |
+ bar will not be inlined into TestH because its definition is unavailable |
+TestH |
+
+
+
+ |
+ |
+inline |
+ foo can be inlined into TestH with cost=30 (threshold=412) |
+TestH |
+
+
+
+ |
+ |
+inline |
+ foo inlined into TestH |
+TestH |
+
+
+
+ |
+ |
+inline |
+ foo can be inlined into TestH with cost=30 (threshold=412) |
+TestH |
+
+
+
+ |
+ |
+inline |
+ foo inlined into TestH |
+TestH |
+
+
+
+16 |
+ |
+ |
+ |
+
+
+
+17 |
+ |
+ |
+ |
+
+
+
+18 |
+ |
+ |
+ |
+
+
+
+
+
+
Index: test/tools/opt-viewer/Outputs/basic/basic_or.c.html
===================================================================
--- /dev/null
+++ test/tools/opt-viewer/Outputs/basic/basic_or.c.html
@@ -0,0 +1,264 @@
+
+
+
+
+
+
+
+
+
+
+Line
+ | Hotness
+ | Optimization
+ | Source
+ | Inline Context
+ |
+
+
+
+
+1 |
+ |
+ |
+ |
+
+
+
+2 |
+ |
+ |
+ |
+
+
+
+ |
+ |
+inline |
+ bar will not be inlined into foo because its definition is unavailable |
+foo |
+
+
+
+3 |
+ |
+ |
+ |
+
+
+
+4 |
+ |
+ |
+ |
+
+
+
+5 |
+ |
+ |
+ |
+
+
+
+6 |
+ |
+ |
+void Test(int *res, int *c, int *d, int *p, int n) { |
+
+
+
+7 |
+ |
+ |
+ |
+
+
+
+8 |
+ |
+ |
+ |
+
+
+
+9 |
+ |
+ |
+#pragma clang loop vectorize(assume_safety) |
+
+
+
+10 |
+ |
+ |
+ for (i = 0; i < 1600; i++) { |
+
+
+
+ |
+ |
+loop-vectorize |
+ vectorized loop (vectorization width: 4, interleaved count: 2) |
+Test |
+
+
+
+11 |
+ |
+ |
+ res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; |
+
+
+
+12 |
+ |
+ |
+ |
+
+
+
+13 |
+ |
+ |
+ |
+
+
+
+14 |
+ |
+ |
+ for (i = 0; i < 16; i++) { |
+
+
+
+ |
+ |
+loop-unroll |
+ completely unrolled loop with 16 iterations |
+Test |
+
+
+
+15 |
+ |
+ |
+ res[i] = (p[i] == 0) ? res[i] : res[i] + d[i]; |
+
+
+
+16 |
+ |
+ |
+ |
+
+
+
+17 |
+ |
+ |
+ |
+
+
+
+18 |
+ |
+ |
+ |
+
+
+
+ |
+ |
+inline |
+ foo can be inlined into Test with cost=30 (threshold=412) |
+Test |
+
+
+
+ |
+ |
+inline |
+ foo inlined into Test |
+Test |
+
+
+
+19 |
+ |
+ |
+ |
+
+
+
+20 |
+ |
+ |
+ |
+
+
+
+ |
+ |
+inline |
+ bar will not be inlined into Test because its definition is unavailable |
+Test |
+
+
+
+ |
+ |
+inline |
+ foo can be inlined into Test with cost=30 (threshold=412) |
+Test |
+
+
+
+ |
+ |
+inline |
+ foo inlined into Test |
+Test |
+
+
+
+ |
+ |
+inline |
+ foo can be inlined into Test with cost=30 (threshold=412) |
+Test |
+
+
+
+ |
+ |
+inline |
+ foo inlined into Test |
+Test |
+
+
+
+21 |
+ |
+ |
+ |
+
+
+
+22 |
+ |
+ |
+ |
+
+
+
+23 |
+ |
+ |
+ |
+
+
+
+
+
+
Index: test/tools/opt-viewer/Outputs/basic/index.html
===================================================================
--- /dev/null
+++ test/tools/opt-viewer/Outputs/basic/index.html
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
+
+
Index: test/tools/opt-viewer/Outputs/basic/style.css
===================================================================
--- /dev/null
+++ test/tools/opt-viewer/Outputs/basic/style.css
@@ -0,0 +1,208 @@
+.source {
+ table-layout: fixed;
+ width: 100%;
+ white-space: nowrap;
+}
+.source td {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.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: test/tools/opt-viewer/basic.test
===================================================================
--- /dev/null
+++ test/tools/opt-viewer/basic.test
@@ -0,0 +1,5 @@
+RUN: %opt-viewer -s %p/Inputs -o %t %p/Inputs/basic/or.yaml
+RUN: diff %p/Outputs/basic/index.html %t/index.html
+RUN: diff %p/Outputs/basic/basic_or.h.html %t/basic_or.h.html
+RUN: diff %p/Outputs/basic/basic_or.c.html %t/basic_or.c.html
+RUN: ls %t/style.css
Index: test/tools/opt-viewer/lit.local.cfg
===================================================================
--- /dev/null
+++ test/tools/opt-viewer/lit.local.cfg
@@ -0,0 +1,2 @@
+if 'have_opt_viewer_modules' not in config.available_features:
+ config.unsupported = True