Add new -Z dump-mir-spanview
option
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
This commit is contained in:
parent
445f34bb14
commit
6b5869a0ae
11 changed files with 739 additions and 0 deletions
|
@ -9,6 +9,7 @@ mod alignment;
|
||||||
pub mod collect_writes;
|
pub mod collect_writes;
|
||||||
mod graphviz;
|
mod graphviz;
|
||||||
pub(crate) mod pretty;
|
pub(crate) mod pretty;
|
||||||
|
pub(crate) mod spanview;
|
||||||
|
|
||||||
pub use self::aggregate::expand_aggregate;
|
pub use self::aggregate::expand_aggregate;
|
||||||
pub use self::alignment::is_disaligned;
|
pub use self::alignment::is_disaligned;
|
||||||
|
|
|
@ -6,6 +6,7 @@ use std::io::{self, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use super::graphviz::write_mir_fn_graphviz;
|
use super::graphviz::write_mir_fn_graphviz;
|
||||||
|
use super::spanview::write_mir_fn_spanview;
|
||||||
use crate::transform::MirSource;
|
use crate::transform::MirSource;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
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)?;
|
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.
|
/// Returns the path to the filename where we should dump a given MIR.
|
||||||
|
|
461
compiler/rustc_mir/src/util/spanview.rs
Normal file
461
compiler/rustc_mir/src/util/spanview.rs
Normal file
|
@ -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("&", "&").replace("<", "<").replace(">", ">")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn escape_attr(s: &str) -> String {
|
||||||
|
s.replace("&", "&")
|
||||||
|
.replace("\"", """)
|
||||||
|
.replace("'", "'")
|
||||||
|
.replace("<", "<")
|
||||||
|
.replace(">", ">")
|
||||||
|
}
|
|
@ -163,6 +163,21 @@ pub enum LtoCli {
|
||||||
Unspecified,
|
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)]
|
#[derive(Clone, PartialEq, Hash)]
|
||||||
pub enum LinkerPluginLto {
|
pub enum LinkerPluginLto {
|
||||||
LinkerPlugin(PathBuf),
|
LinkerPlugin(PathBuf),
|
||||||
|
|
|
@ -255,6 +255,7 @@ macro_rules! options {
|
||||||
pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`";
|
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_linker_flavor: &str = ::rustc_target::spec::LinkerFlavor::one_of();
|
||||||
pub const parse_optimization_fuel: &str = "crate=integer";
|
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_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_treat_err_as_bug: &str = "either no value or a number bigger than 0";
|
||||||
pub const parse_lto: &str =
|
pub const parse_lto: &str =
|
||||||
|
@ -551,6 +552,36 @@ macro_rules! options {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
fn parse_treat_err_as_bug(slot: &mut Option<usize>, v: Option<&str>) -> bool {
|
||||||
match v {
|
match v {
|
||||||
Some(s) => { *slot = s.parse().ok().filter(|&x| x != 0); slot.unwrap_or(0) != 0 }
|
Some(s) => { *slot = s.parse().ok().filter(|&x| x != 0); slot.unwrap_or(0) != 0 }
|
||||||
|
@ -849,6 +880,11 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
||||||
"exclude the pass number when dumping MIR (used in tests) (default: no)"),
|
"exclude the pass number when dumping MIR (used in tests) (default: no)"),
|
||||||
dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED],
|
dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED],
|
||||||
"in addition to `.mir` files, create graphviz `.dot` files (default: no)"),
|
"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_stack_sizes: bool = (false, parse_bool, [UNTRACKED],
|
||||||
"emit a section containing stack size metadata (default: no)"),
|
"emit a section containing stack size metadata (default: no)"),
|
||||||
fewer_names: bool = (false, parse_bool, [TRACKED],
|
fewer_names: bool = (false, parse_bool, [TRACKED],
|
||||||
|
|
5
src/test/mir-opt/spanview-block.rs
Normal file
5
src/test/mir-opt/spanview-block.rs
Normal file
|
@ -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() {}
|
5
src/test/mir-opt/spanview-statement.rs
Normal file
5
src/test/mir-opt/spanview-statement.rs
Normal file
|
@ -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() {}
|
5
src/test/mir-opt/spanview-terminator.rs
Normal file
5
src/test/mir-opt/spanview-terminator.rs
Normal file
|
@ -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() {}
|
67
src/test/mir-opt/spanview_block.main.mir_map.0.html
Normal file
67
src/test/mir-opt/spanview_block.main.mir_map.0.html
Normal file
|
@ -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 -> 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>
|
67
src/test/mir-opt/spanview_statement.main.mir_map.0.html
Normal file
67
src/test/mir-opt/spanview_statement.main.mir_map.0.html
Normal file
|
@ -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 -> 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>
|
66
src/test/mir-opt/spanview_terminator.main.mir_map.0.html
Normal file
66
src/test/mir-opt/spanview_terminator.main.mir_map.0.html
Normal file
|
@ -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 -> 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>
|
Loading…
Add table
Add a link
Reference in a new issue