]> git.lizzy.rs Git - rust.git/commitdiff
Add new `-Z dump-mir-spanview` option
authorRich Kadel <richkadel@google.com>
Sat, 29 Aug 2020 17:55:46 +0000 (10:55 -0700)
committerRich Kadel <richkadel@google.com>
Tue, 1 Sep 2020 05:57:55 +0000 (22:57 -0700)
Similar to `-Z dump-mir-graphviz`, this adds the option to write
HTML+CSS files that allow users to analyze the spans associated with MIR
elements (by individual statement, just terminator, or overall basic
block).

This PR was split out from PR #76004, and exposes an API for spanview
HTML+CSS files that is also used to analyze code regions chosen for
coverage instrumentation (in a follow-on PR).

Rust compiler MCP rust-lang/compiler-team#278

Relevant issue: #34701 - Implement support for LLVMs code coverage
instrumentation

compiler/rustc_mir/src/util/mod.rs
compiler/rustc_mir/src/util/pretty.rs
compiler/rustc_mir/src/util/spanview.rs [new file with mode: 0644]
compiler/rustc_session/src/config.rs
compiler/rustc_session/src/options.rs
src/test/mir-opt/spanview-block.rs [new file with mode: 0644]
src/test/mir-opt/spanview-statement.rs [new file with mode: 0644]
src/test/mir-opt/spanview-terminator.rs [new file with mode: 0644]
src/test/mir-opt/spanview_block.main.mir_map.0.html [new file with mode: 0644]
src/test/mir-opt/spanview_statement.main.mir_map.0.html [new file with mode: 0644]
src/test/mir-opt/spanview_terminator.main.mir_map.0.html [new file with mode: 0644]

index 8bbe207c077ee23e7957c3c1134557111508d453..ed0fafb1aac16f32ba96c892e6a6c719986b25bb 100644 (file)
@@ -9,6 +9,7 @@
 pub mod collect_writes;
 mod graphviz;
 pub(crate) mod pretty;
+pub(crate) mod spanview;
 
 pub use self::aggregate::expand_aggregate;
 pub use self::alignment::is_disaligned;
index 2a9cbc7fc0e37915388dfff1a037ccf561a3b405..db57766620e8134964add64d72678cb17369dcc5 100644 (file)
@@ -6,6 +6,7 @@
 use std::path::{Path, PathBuf};
 
 use super::graphviz::write_mir_fn_graphviz;
+use super::spanview::write_mir_fn_spanview;
 use crate::transform::MirSource;
 use either::Either;
 use rustc_data_structures::fx::FxHashMap;
@@ -147,6 +148,16 @@ fn dump_matched_mir_node<'tcx, F>(
             write_mir_fn_graphviz(tcx, source.def_id(), body, false, &mut file)?;
         };
     }
+
+    if let Some(spanview) = tcx.sess.opts.debugging_opts.dump_mir_spanview {
+        let _: io::Result<()> = try {
+            let mut file =
+                create_dump_file(tcx, "html", pass_num, pass_name, disambiguator, source)?;
+            if source.def_id().is_local() {
+                write_mir_fn_spanview(tcx, source.def_id(), body, spanview, &mut file)?;
+            }
+        };
+    }
 }
 
 /// Returns the path to the filename where we should dump a given MIR.
diff --git a/compiler/rustc_mir/src/util/spanview.rs b/compiler/rustc_mir/src/util/spanview.rs
new file mode 100644 (file)
index 0000000..b2f2b5f
--- /dev/null
@@ -0,0 +1,461 @@
+use rustc_hir::def_id::DefId;
+use rustc_middle::hir;
+use rustc_middle::mir::*;
+use rustc_middle::ty::TyCtxt;
+use rustc_session::config::MirSpanview;
+use rustc_span::{BytePos, Pos, Span};
+
+use std::io::{self, Write};
+use std::iter::Peekable;
+
+pub const TOOLTIP_INDENT: &str = "    ";
+
+const NEW_LINE_SPAN: &str = "</span>\n<span class=\"line\">";
+const HEADER: &str = r#"<!DOCTYPE html>
+<html>
+<head>
+    <title>coverage_of_if_else - Code Regions</title>
+    <style>
+    .line {
+        counter-increment: line;
+    }
+    .line:before {
+        content: counter(line) ": ";
+        font-family: Menlo, Monaco, monospace;
+        font-style: italic;
+        width: 3.8em;
+        display: inline-block;
+        text-align: right;
+        filter: opacity(50%);
+        -webkit-user-select: none;
+    }
+    .code {
+        color: #dddddd;
+        background-color: #222222;
+        font-family: Menlo, Monaco, monospace;
+        line-height: 1.4em;
+        border-bottom: 2px solid #222222;
+        white-space: pre;
+        display: inline-block;
+    }
+    .odd {
+        background-color: #55bbff;
+        color: #223311;
+    }
+    .even {
+        background-color: #ee7756;
+        color: #551133;
+    }
+    .code {
+        --index: calc(var(--layer) - 1);
+        padding-top: calc(var(--index) * 0.15em);
+        filter:
+            hue-rotate(calc(var(--index) * 25deg))
+            saturate(calc(100% - (var(--index) * 2%)))
+            brightness(calc(100% - (var(--index) * 1.5%)));
+    }
+    .annotation {
+        color: #4444ff;
+        font-family: monospace;
+        font-style: italic;
+        display: none;
+        -webkit-user-select: none;
+    }
+    body:active .annotation {
+        /* requires holding mouse down anywhere on the page */
+        display: inline-block;
+    }
+    span:hover .annotation {
+        /* requires hover over a span ONLY on its first line */
+        display: inline-block;
+    }
+    </style>
+</head>
+<body>"#;
+
+const FOOTER: &str = r#"
+</body>
+</html>"#;
+
+/// Metadata to highlight the span of a MIR BasicBlock, Statement, or Terminator.
+pub struct SpanViewable {
+    pub span: Span,
+    pub title: String,
+    pub tooltip: String,
+}
+
+/// Write a spanview HTML+CSS file to analyze MIR element spans.
+pub fn write_mir_fn_spanview<'tcx, W>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    body: &Body<'tcx>,
+    spanview: MirSpanview,
+    w: &mut W,
+) -> io::Result<()>
+where
+    W: Write,
+{
+    let body_span = hir_body(tcx, def_id).value.span;
+    let mut span_viewables = Vec::new();
+    for (bb, data) in body.basic_blocks().iter_enumerated() {
+        match spanview {
+            MirSpanview::Statement => {
+                for (i, statement) in data.statements.iter().enumerate() {
+                    if let Some(span_viewable) =
+                        statement_span_viewable(tcx, body_span, bb, i, statement)
+                    {
+                        span_viewables.push(span_viewable);
+                    }
+                }
+                if let Some(span_viewable) = terminator_span_viewable(tcx, body_span, bb, data) {
+                    span_viewables.push(span_viewable);
+                }
+            }
+            MirSpanview::Terminator => {
+                if let Some(span_viewable) = terminator_span_viewable(tcx, body_span, bb, data) {
+                    span_viewables.push(span_viewable);
+                }
+            }
+            MirSpanview::Block => {
+                if let Some(span_viewable) = block_span_viewable(tcx, body_span, bb, data) {
+                    span_viewables.push(span_viewable);
+                }
+            }
+        }
+    }
+    write_spanview_document(tcx, def_id, span_viewables, w)?;
+    Ok(())
+}
+
+/// Generate a spanview HTML+CSS document for the given local function `def_id`, and a pre-generated
+/// list `SpanViewable`s.
+pub fn write_spanview_document<'tcx, W>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    mut span_viewables: Vec<SpanViewable>,
+    w: &mut W,
+) -> io::Result<()>
+where
+    W: Write,
+{
+    let fn_span = fn_span(tcx, def_id);
+    writeln!(w, "{}", HEADER)?;
+    let mut next_pos = fn_span.lo();
+    let end_pos = fn_span.hi();
+    let source_map = tcx.sess.source_map();
+    let start = source_map.lookup_char_pos(next_pos);
+    write!(
+        w,
+        r#"<div class="code" style="counter-reset: line {}"><span class="line">{}"#,
+        start.line - 1,
+        " ".repeat(start.col.to_usize())
+    )?;
+    span_viewables.sort_unstable_by(|a, b| {
+        let a = a.span;
+        let b = b.span;
+        if a.lo() == b.lo() {
+            // Sort hi() in reverse order so shorter spans are attempted after longer spans.
+            // This should give shorter spans a higher "layer", so they are not covered by
+            // the longer spans.
+            b.hi().partial_cmp(&a.hi())
+        } else {
+            a.lo().partial_cmp(&b.lo())
+        }
+        .unwrap()
+    });
+    let mut ordered_span_viewables = span_viewables.iter().peekable();
+    let mut alt = false;
+    while ordered_span_viewables.peek().is_some() {
+        next_pos = write_span_viewables(tcx, next_pos, &mut ordered_span_viewables, false, 1, w)?;
+        alt = !alt;
+    }
+    if next_pos < end_pos {
+        write_coverage_gap(tcx, next_pos, end_pos, w)?;
+    }
+    write!(w, r#"</span></div>"#)?;
+    writeln!(w, "{}", FOOTER)?;
+    Ok(())
+}
+
+/// Format a string showing the start line and column, and end line and column within a file.
+pub fn source_range_no_file<'tcx>(tcx: TyCtxt<'tcx>, span: &Span) -> String {
+    let source_map = tcx.sess.source_map();
+    let start = source_map.lookup_char_pos(span.lo());
+    let end = source_map.lookup_char_pos(span.hi());
+    format!("{}:{}-{}:{}", start.line, start.col.to_usize() + 1, end.line, end.col.to_usize() + 1)
+}
+
+pub fn statement_kind_name(statement: &Statement<'_>) -> &'static str {
+    use StatementKind::*;
+    match statement.kind {
+        Assign(..) => "Assign",
+        FakeRead(..) => "FakeRead",
+        SetDiscriminant { .. } => "SetDiscriminant",
+        StorageLive(..) => "StorageLive",
+        StorageDead(..) => "StorageDead",
+        LlvmInlineAsm(..) => "LlvmInlineAsm",
+        Retag(..) => "Retag",
+        AscribeUserType(..) => "AscribeUserType",
+        Coverage(..) => "Coverage",
+        Nop => "Nop",
+    }
+}
+
+pub fn terminator_kind_name(term: &Terminator<'_>) -> &'static str {
+    use TerminatorKind::*;
+    match term.kind {
+        Goto { .. } => "Goto",
+        SwitchInt { .. } => "SwitchInt",
+        Resume => "Resume",
+        Abort => "Abort",
+        Return => "Return",
+        Unreachable => "Unreachable",
+        Drop { .. } => "Drop",
+        DropAndReplace { .. } => "DropAndReplace",
+        Call { .. } => "Call",
+        Assert { .. } => "Assert",
+        Yield { .. } => "Yield",
+        GeneratorDrop => "GeneratorDrop",
+        FalseEdge { .. } => "FalseEdge",
+        FalseUnwind { .. } => "FalseUnwind",
+        InlineAsm { .. } => "InlineAsm",
+    }
+}
+
+fn statement_span_viewable<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body_span: Span,
+    bb: BasicBlock,
+    i: usize,
+    statement: &Statement<'tcx>,
+) -> Option<SpanViewable> {
+    let span = statement.source_info.span;
+    if !body_span.contains(span) {
+        return None;
+    }
+    let title = format!("bb{}[{}]", bb.index(), i);
+    let tooltip = tooltip(tcx, &title, span, vec![statement.clone()], &None);
+    Some(SpanViewable { span, title, tooltip })
+}
+
+fn terminator_span_viewable<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body_span: Span,
+    bb: BasicBlock,
+    data: &BasicBlockData<'tcx>,
+) -> Option<SpanViewable> {
+    let term = data.terminator();
+    let span = term.source_info.span;
+    if !body_span.contains(span) {
+        return None;
+    }
+    let title = format!("bb{}`{}`", bb.index(), terminator_kind_name(term));
+    let tooltip = tooltip(tcx, &title, span, vec![], &data.terminator);
+    Some(SpanViewable { span, title, tooltip })
+}
+
+fn block_span_viewable<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body_span: Span,
+    bb: BasicBlock,
+    data: &BasicBlockData<'tcx>,
+) -> Option<SpanViewable> {
+    let span = compute_block_span(data, body_span);
+    if !body_span.contains(span) {
+        return None;
+    }
+    let title = format!("bb{}", bb.index());
+    let tooltip = tooltip(tcx, &title, span, data.statements.clone(), &data.terminator);
+    Some(SpanViewable { span, title, tooltip })
+}
+
+fn compute_block_span<'tcx>(data: &BasicBlockData<'tcx>, body_span: Span) -> Span {
+    let mut span = data.terminator().source_info.span;
+    for statement_span in data.statements.iter().map(|statement| statement.source_info.span) {
+        // Only combine Spans from the function's body_span.
+        if body_span.contains(statement_span) {
+            span = span.to(statement_span);
+        }
+    }
+    span
+}
+
+/// Recursively process each ordered span. Spans that overlap will have progressively varying
+/// styles, such as increased padding for each overlap. Non-overlapping adjacent spans will
+/// have alternating style choices, to help distinguish between them if, visually adjacent.
+/// The `layer` is incremented for each overlap, and the `alt` bool alternates between true
+/// and false, for each adjacent non-overlapping span. Source code between the spans (code
+/// that is not in any coverage region) has neutral styling.
+fn write_span_viewables<'tcx, 'b, W>(
+    tcx: TyCtxt<'tcx>,
+    next_pos: BytePos,
+    ordered_span_viewables: &mut Peekable<impl Iterator<Item = &'b SpanViewable>>,
+    alt: bool,
+    layer: usize,
+    w: &mut W,
+) -> io::Result<BytePos>
+where
+    W: Write,
+{
+    let span_viewable =
+        ordered_span_viewables.next().expect("ordered_span_viewables should have some");
+    if next_pos < span_viewable.span.lo() {
+        write_coverage_gap(tcx, next_pos, span_viewable.span.lo(), w)?;
+    }
+    let mut remaining_span = span_viewable.span;
+    let mut subalt = false;
+    loop {
+        let next_span_viewable = match ordered_span_viewables.peek() {
+            None => break,
+            Some(span_viewable) => *span_viewable,
+        };
+        if !next_span_viewable.span.overlaps(remaining_span) {
+            break;
+        }
+        write_span(
+            tcx,
+            remaining_span.until(next_span_viewable.span),
+            Some(span_viewable),
+            alt,
+            layer,
+            w,
+        )?;
+        let next_pos = write_span_viewables(
+            tcx,
+            next_span_viewable.span.lo(),
+            ordered_span_viewables,
+            subalt,
+            layer + 1,
+            w,
+        )?;
+        subalt = !subalt;
+        if next_pos < remaining_span.hi() {
+            remaining_span = remaining_span.with_lo(next_pos);
+        } else {
+            return Ok(next_pos);
+        }
+    }
+    write_span(tcx, remaining_span, Some(span_viewable), alt, layer, w)
+}
+
+fn write_coverage_gap<'tcx, W>(
+    tcx: TyCtxt<'tcx>,
+    lo: BytePos,
+    hi: BytePos,
+    w: &mut W,
+) -> io::Result<BytePos>
+where
+    W: Write,
+{
+    write_span(tcx, Span::with_root_ctxt(lo, hi), None, false, 0, w)
+}
+
+fn write_span<'tcx, W>(
+    tcx: TyCtxt<'tcx>,
+    span: Span,
+    span_viewable: Option<&SpanViewable>,
+    alt: bool,
+    layer: usize,
+    w: &mut W,
+) -> io::Result<BytePos>
+where
+    W: Write,
+{
+    let source_map = tcx.sess.source_map();
+    let snippet = source_map
+        .span_to_snippet(span)
+        .unwrap_or_else(|err| bug!("span_to_snippet error for span {:?}: {:?}", span, err));
+    let labeled_snippet = if let Some(SpanViewable { title, .. }) = span_viewable {
+        if span.is_empty() {
+            format!(r#"<span class="annotation">@{}</span>"#, title)
+        } else {
+            format!(r#"<span class="annotation">@{}:</span> {}"#, title, escape_html(&snippet))
+        }
+    } else {
+        snippet
+    };
+    let maybe_alt = if layer > 0 {
+        if alt { " odd" } else { " even" }
+    } else {
+        ""
+    };
+    let maybe_tooltip = if let Some(SpanViewable { tooltip, .. }) = span_viewable {
+        format!(" title=\"{}\"", escape_attr(tooltip))
+    } else {
+        "".to_owned()
+    };
+    if layer == 1 {
+        write!(w, "<span>")?;
+    }
+    for (i, line) in labeled_snippet.lines().enumerate() {
+        if i > 0 {
+            write!(w, "{}", NEW_LINE_SPAN)?;
+        }
+        write!(
+            w,
+            r#"<span class="code{}" style="--layer: {}"{}>{}</span>"#,
+            maybe_alt, layer, maybe_tooltip, line
+        )?;
+    }
+    if layer == 1 {
+        write!(w, "</span>")?;
+    }
+    Ok(span.hi())
+}
+
+fn tooltip<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    title: &str,
+    span: Span,
+    statements: Vec<Statement<'tcx>>,
+    terminator: &Option<Terminator<'tcx>>,
+) -> String {
+    let source_map = tcx.sess.source_map();
+    let mut text = Vec::new();
+    text.push(format!("{}: {}:", title, &source_map.span_to_string(span)));
+    for statement in statements {
+        let source_range = source_range_no_file(tcx, &statement.source_info.span);
+        text.push(format!(
+            "\n{}{}: {}: {}",
+            TOOLTIP_INDENT,
+            source_range,
+            statement_kind_name(&statement),
+            format!("{:?}", statement)
+        ));
+    }
+    if let Some(term) = terminator {
+        let source_range = source_range_no_file(tcx, &term.source_info.span);
+        text.push(format!(
+            "\n{}{}: {}: {:?}",
+            TOOLTIP_INDENT,
+            source_range,
+            terminator_kind_name(term),
+            term.kind
+        ));
+    }
+    text.join("")
+}
+
+fn fn_span<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Span {
+    let hir_id =
+        tcx.hir().local_def_id_to_hir_id(def_id.as_local().expect("expected DefId is local"));
+    tcx.hir().span(hir_id)
+}
+
+fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> {
+    let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local");
+    let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body");
+    tcx.hir().body(fn_body_id)
+}
+
+fn escape_html(s: &str) -> String {
+    s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
+}
+
+fn escape_attr(s: &str) -> String {
+    s.replace("&", "&amp;")
+        .replace("\"", "&quot;")
+        .replace("'", "&#39;")
+        .replace("<", "&lt;")
+        .replace(">", "&gt;")
+}
index 0a2a535598a2ff8c66da875f209c14cc9f5fba79..6861314a88f6fc1843c7f731fb1dcb71d3cfba5a 100644 (file)
@@ -163,6 +163,21 @@ pub enum LtoCli {
     Unspecified,
 }
 
+/// The different settings that the `-Z dump_mir_spanview` flag can have. `Statement` generates a
+/// document highlighting each span of every statement (including terminators). `Terminator` and
+/// `Block` highlight a single span per `BasicBlock`: the span of the block's `Terminator`, or a
+/// computed span for the block, representing the entire range, covering the block's terminator and
+/// all of its statements.
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum MirSpanview {
+    /// Default `-Z dump_mir_spanview` or `-Z dump_mir_spanview=statement`
+    Statement,
+    /// `-Z dump_mir_spanview=terminator`
+    Terminator,
+    /// `-Z dump_mir_spanview=block`
+    Block,
+}
+
 #[derive(Clone, PartialEq, Hash)]
 pub enum LinkerPluginLto {
     LinkerPlugin(PathBuf),
index ee30c16108a0c50d51f3ce0e450ff0a2f6342ed2..bcf65a1c4d2b0571a3c81c319d954f148cd448b5 100644 (file)
@@ -255,6 +255,7 @@ mod $mod_desc {
         pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`";
         pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavor::one_of();
         pub const parse_optimization_fuel: &str = "crate=integer";
+        pub const parse_mir_spanview: &str = "`statement` (default), `terminator`, or `block`";
         pub const parse_unpretty: &str = "`string` or `string=string`";
         pub const parse_treat_err_as_bug: &str = "either no value or a number bigger than 0";
         pub const parse_lto: &str =
@@ -551,6 +552,36 @@ fn parse_unpretty(slot: &mut Option<String>, v: Option<&str>) -> bool {
             }
         }
 
+        fn parse_mir_spanview(slot: &mut Option<MirSpanview>, v: Option<&str>) -> bool {
+            if v.is_some() {
+                let mut bool_arg = None;
+                if parse_opt_bool(&mut bool_arg, v) {
+                    *slot = if bool_arg.unwrap() {
+                        Some(MirSpanview::Statement)
+                    } else {
+                        None
+                    };
+                    return true
+                }
+            }
+
+            let v = match v {
+                None => {
+                    *slot = Some(MirSpanview::Statement);
+                    return true;
+                }
+                Some(v) => v,
+            };
+
+            *slot = Some(match v.trim_end_matches("s") {
+                "statement" | "stmt" => MirSpanview::Statement,
+                "terminator" | "term" => MirSpanview::Terminator,
+                "block" | "basicblock" => MirSpanview::Block,
+                _ => return false,
+            });
+            true
+        }
+
         fn parse_treat_err_as_bug(slot: &mut Option<usize>, v: Option<&str>) -> bool {
             match v {
                 Some(s) => { *slot = s.parse().ok().filter(|&x| x != 0); slot.unwrap_or(0) != 0 }
@@ -849,6 +880,11 @@ fn parse_target_feature(slot: &mut String, v: Option<&str>) -> bool {
         "exclude the pass number when dumping MIR (used in tests) (default: no)"),
     dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED],
         "in addition to `.mir` files, create graphviz `.dot` files (default: no)"),
+    dump_mir_spanview: Option<MirSpanview> = (None, parse_mir_spanview, [UNTRACKED],
+        "in addition to `.mir` files, create `.html` files to view spans for \
+        all `statement`s (including terminators), only `terminator` spans, or \
+        computed `block` spans (one span encompassing a block's terminator and \
+        all statements)."),
     emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED],
         "emit a section containing stack size metadata (default: no)"),
     fewer_names: bool = (false, parse_bool, [TRACKED],
diff --git a/src/test/mir-opt/spanview-block.rs b/src/test/mir-opt/spanview-block.rs
new file mode 100644 (file)
index 0000000..fc1d6e0
--- /dev/null
@@ -0,0 +1,5 @@
+// Test spanview block output
+// compile-flags: -Z dump-mir-spanview=block
+
+// EMIT_MIR spanview_block.main.mir_map.0.html
+fn main() {}
diff --git a/src/test/mir-opt/spanview-statement.rs b/src/test/mir-opt/spanview-statement.rs
new file mode 100644 (file)
index 0000000..a43ad5e
--- /dev/null
@@ -0,0 +1,5 @@
+// Test spanview output (the default value for `-Z dump-mir-spanview` is "statement")
+// compile-flags: -Z dump-mir-spanview
+
+// EMIT_MIR spanview_statement.main.mir_map.0.html
+fn main() {}
diff --git a/src/test/mir-opt/spanview-terminator.rs b/src/test/mir-opt/spanview-terminator.rs
new file mode 100644 (file)
index 0000000..92e1411
--- /dev/null
@@ -0,0 +1,5 @@
+// Test spanview terminator output
+// compile-flags: -Z dump-mir-spanview=terminator
+
+// EMIT_MIR spanview_terminator.main.mir_map.0.html
+fn main() {}
diff --git a/src/test/mir-opt/spanview_block.main.mir_map.0.html b/src/test/mir-opt/spanview_block.main.mir_map.0.html
new file mode 100644 (file)
index 0000000..7c1b7bc
--- /dev/null
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>coverage_of_if_else - Code Regions</title>
+    <style>
+    .line {
+        counter-increment: line;
+    }
+    .line:before {
+        content: counter(line) ": ";
+        font-family: Menlo, Monaco, monospace;
+        font-style: italic;
+        width: 3.8em;
+        display: inline-block;
+        text-align: right;
+        filter: opacity(50%);
+        -webkit-user-select: none;
+    }
+    .code {
+        color: #dddddd;
+        background-color: #222222;
+        font-family: Menlo, Monaco, monospace;
+        line-height: 1.4em;
+        border-bottom: 2px solid #222222;
+        white-space: pre;
+        display: inline-block;
+    }
+    .odd {
+        background-color: #55bbff;
+        color: #223311;
+    }
+    .even {
+        background-color: #ee7756;
+        color: #551133;
+    }
+    .code {
+        --index: calc(var(--layer) - 1);
+        padding-top: calc(var(--index) * 0.15em);
+        filter:
+            hue-rotate(calc(var(--index) * 25deg))
+            saturate(calc(100% - (var(--index) * 2%)))
+            brightness(calc(100% - (var(--index) * 1.5%)));
+    }
+    .annotation {
+        color: #4444ff;
+        font-family: monospace;
+        font-style: italic;
+        display: none;
+        -webkit-user-select: none;
+    }
+    body:active .annotation {
+        /* requires holding mouse down anywhere on the page */
+        display: inline-block;
+    }
+    span:hover .annotation {
+        /* requires hover over a span ONLY on its first line */
+        display: inline-block;
+    }
+    </style>
+</head>
+<body>
+<div class="code" style="counter-reset: line 4"><span class="line"><span class="code" style="--layer: 0">fn main() </span><span><span class="code even" style="--layer: 1" title="bb0: $DIR/spanview-block.rs:5:11: 5:13:
+    5:11-5:13: Assign: _0 = const ()
+    5:13-5:13: Goto: goto -&gt; bb2"><span class="annotation">@bb0:</span> {}</span></span><span><span class="code even" style="--layer: 1" title="bb2: $DIR/spanview-block.rs:5:13: 5:13:
+    5:13-5:13: Return: return"><span class="annotation">@bb2</span></span></span></span></div>
+</body>
+</html>
diff --git a/src/test/mir-opt/spanview_statement.main.mir_map.0.html b/src/test/mir-opt/spanview_statement.main.mir_map.0.html
new file mode 100644 (file)
index 0000000..f8662a3
--- /dev/null
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>coverage_of_if_else - Code Regions</title>
+    <style>
+    .line {
+        counter-increment: line;
+    }
+    .line:before {
+        content: counter(line) ": ";
+        font-family: Menlo, Monaco, monospace;
+        font-style: italic;
+        width: 3.8em;
+        display: inline-block;
+        text-align: right;
+        filter: opacity(50%);
+        -webkit-user-select: none;
+    }
+    .code {
+        color: #dddddd;
+        background-color: #222222;
+        font-family: Menlo, Monaco, monospace;
+        line-height: 1.4em;
+        border-bottom: 2px solid #222222;
+        white-space: pre;
+        display: inline-block;
+    }
+    .odd {
+        background-color: #55bbff;
+        color: #223311;
+    }
+    .even {
+        background-color: #ee7756;
+        color: #551133;
+    }
+    .code {
+        --index: calc(var(--layer) - 1);
+        padding-top: calc(var(--index) * 0.15em);
+        filter:
+            hue-rotate(calc(var(--index) * 25deg))
+            saturate(calc(100% - (var(--index) * 2%)))
+            brightness(calc(100% - (var(--index) * 1.5%)));
+    }
+    .annotation {
+        color: #4444ff;
+        font-family: monospace;
+        font-style: italic;
+        display: none;
+        -webkit-user-select: none;
+    }
+    body:active .annotation {
+        /* requires holding mouse down anywhere on the page */
+        display: inline-block;
+    }
+    span:hover .annotation {
+        /* requires hover over a span ONLY on its first line */
+        display: inline-block;
+    }
+    </style>
+</head>
+<body>
+<div class="code" style="counter-reset: line 4"><span class="line"><span class="code" style="--layer: 0">fn main() </span><span><span class="code even" style="--layer: 1" title="bb0[0]: $DIR/spanview-statement.rs:5:11: 5:13:
+    5:11-5:13: Assign: _0 = const ()"><span class="annotation">@bb0[0]:</span> {}</span></span><span><span class="code even" style="--layer: 1" title="bb0`Goto`: $DIR/spanview-statement.rs:5:13: 5:13:
+    5:13-5:13: Goto: goto -&gt; bb2"><span class="annotation">@bb0`Goto`</span></span></span><span><span class="code even" style="--layer: 1" title="bb2`Return`: $DIR/spanview-statement.rs:5:13: 5:13:
+    5:13-5:13: Return: return"><span class="annotation">@bb2`Return`</span></span></span></span></div>
+</body>
+</html>
diff --git a/src/test/mir-opt/spanview_terminator.main.mir_map.0.html b/src/test/mir-opt/spanview_terminator.main.mir_map.0.html
new file mode 100644 (file)
index 0000000..d0a11a8
--- /dev/null
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>coverage_of_if_else - Code Regions</title>
+    <style>
+    .line {
+        counter-increment: line;
+    }
+    .line:before {
+        content: counter(line) ": ";
+        font-family: Menlo, Monaco, monospace;
+        font-style: italic;
+        width: 3.8em;
+        display: inline-block;
+        text-align: right;
+        filter: opacity(50%);
+        -webkit-user-select: none;
+    }
+    .code {
+        color: #dddddd;
+        background-color: #222222;
+        font-family: Menlo, Monaco, monospace;
+        line-height: 1.4em;
+        border-bottom: 2px solid #222222;
+        white-space: pre;
+        display: inline-block;
+    }
+    .odd {
+        background-color: #55bbff;
+        color: #223311;
+    }
+    .even {
+        background-color: #ee7756;
+        color: #551133;
+    }
+    .code {
+        --index: calc(var(--layer) - 1);
+        padding-top: calc(var(--index) * 0.15em);
+        filter:
+            hue-rotate(calc(var(--index) * 25deg))
+            saturate(calc(100% - (var(--index) * 2%)))
+            brightness(calc(100% - (var(--index) * 1.5%)));
+    }
+    .annotation {
+        color: #4444ff;
+        font-family: monospace;
+        font-style: italic;
+        display: none;
+        -webkit-user-select: none;
+    }
+    body:active .annotation {
+        /* requires holding mouse down anywhere on the page */
+        display: inline-block;
+    }
+    span:hover .annotation {
+        /* requires hover over a span ONLY on its first line */
+        display: inline-block;
+    }
+    </style>
+</head>
+<body>
+<div class="code" style="counter-reset: line 4"><span class="line"><span class="code" style="--layer: 0">fn main() {}</span><span><span class="code even" style="--layer: 1" title="bb0`Goto`: $DIR/spanview-terminator.rs:5:13: 5:13:
+    5:13-5:13: Goto: goto -&gt; bb2"><span class="annotation">@bb0`Goto`</span></span></span><span><span class="code even" style="--layer: 1" title="bb2`Return`: $DIR/spanview-terminator.rs:5:13: 5:13:
+    5:13-5:13: Return: return"><span class="annotation">@bb2`Return`</span></span></span></span></div>
+</body>
+</html>