mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 08:44:35 +00:00
Rollup merge of #119208 - Zalathar:hoist, r=WaffleLapkin,Swatinem
coverage: Hoist some complex code out of the main span refinement loop The span refinement loop in `spans.rs` takes the spans that have been extracted from MIR, and modifies them to produce more helpful output in coverage reports. It is also one of the most complicated pieces of code in the coverage instrumentor. It has an abundance of moving pieces that make it difficult to understand, and most attempts to modify it end up accidentally changing its behaviour in unacceptable ways. This PR nevertheless tries to make a dent in it by hoisting two pieces of special-case logic out of the main loop, and into separate preprocessing passes. Coverage tests show that the resulting mappings are *almost* identical, with all known differences being unimportant. This should hopefully unlock further simplifications to the refinement loop, since it now has fewer edge cases to worry about.
This commit is contained in:
commit
bacddd3e5d
@ -1,11 +1,9 @@
|
||||
use std::cell::OnceCell;
|
||||
|
||||
use rustc_data_structures::graph::WithNumNodes;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir;
|
||||
use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol, DUMMY_SP};
|
||||
use rustc_span::{BytePos, Span, DUMMY_SP};
|
||||
|
||||
use super::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
|
||||
use super::graph::{BasicCoverageBlock, CoverageGraph};
|
||||
use crate::coverage::ExtractedHirInfo;
|
||||
|
||||
mod from_mir;
|
||||
@ -70,35 +68,17 @@ impl CoverageSpans {
|
||||
/// `dominates()` the `BasicBlock`s in this `CoverageSpan`.
|
||||
#[derive(Debug, Clone)]
|
||||
struct CoverageSpan {
|
||||
pub span: Span,
|
||||
pub expn_span: Span,
|
||||
pub current_macro_or_none: OnceCell<Option<Symbol>>,
|
||||
pub bcb: BasicCoverageBlock,
|
||||
span: Span,
|
||||
bcb: BasicCoverageBlock,
|
||||
/// List of all the original spans from MIR that have been merged into this
|
||||
/// span. Mainly used to precisely skip over gaps when truncating a span.
|
||||
pub merged_spans: Vec<Span>,
|
||||
pub is_closure: bool,
|
||||
merged_spans: Vec<Span>,
|
||||
is_closure: bool,
|
||||
}
|
||||
|
||||
impl CoverageSpan {
|
||||
pub fn for_fn_sig(fn_sig_span: Span) -> Self {
|
||||
Self::new(fn_sig_span, fn_sig_span, START_BCB, false)
|
||||
}
|
||||
|
||||
pub(super) fn new(
|
||||
span: Span,
|
||||
expn_span: Span,
|
||||
bcb: BasicCoverageBlock,
|
||||
is_closure: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
span,
|
||||
expn_span,
|
||||
current_macro_or_none: Default::default(),
|
||||
bcb,
|
||||
merged_spans: vec![span],
|
||||
is_closure,
|
||||
}
|
||||
fn new(span: Span, bcb: BasicCoverageBlock, is_closure: bool) -> Self {
|
||||
Self { span, bcb, merged_spans: vec![span], is_closure }
|
||||
}
|
||||
|
||||
pub fn merge_from(&mut self, other: &Self) {
|
||||
@ -123,37 +103,6 @@ impl CoverageSpan {
|
||||
pub fn is_in_same_bcb(&self, other: &Self) -> bool {
|
||||
self.bcb == other.bcb
|
||||
}
|
||||
|
||||
/// If the span is part of a macro, returns the macro name symbol.
|
||||
pub fn current_macro(&self) -> Option<Symbol> {
|
||||
self.current_macro_or_none
|
||||
.get_or_init(|| {
|
||||
if let ExpnKind::Macro(MacroKind::Bang, current_macro) =
|
||||
self.expn_span.ctxt().outer_expn_data().kind
|
||||
{
|
||||
return Some(current_macro);
|
||||
}
|
||||
None
|
||||
})
|
||||
.map(|symbol| symbol)
|
||||
}
|
||||
|
||||
/// If the span is part of a macro, and the macro is visible (expands directly to the given
|
||||
/// body_span), returns the macro name symbol.
|
||||
pub fn visible_macro(&self, body_span: Span) -> Option<Symbol> {
|
||||
let current_macro = self.current_macro()?;
|
||||
let parent_callsite = self.expn_span.parent_callsite()?;
|
||||
|
||||
// In addition to matching the context of the body span, the parent callsite
|
||||
// must also be the source callsite, i.e. the parent must have no parent.
|
||||
let is_visible_macro =
|
||||
parent_callsite.parent_callsite().is_none() && parent_callsite.eq_ctxt(body_span);
|
||||
is_visible_macro.then_some(current_macro)
|
||||
}
|
||||
|
||||
pub fn is_macro_expansion(&self) -> bool {
|
||||
self.current_macro().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the initial set of `CoverageSpan`s (one per MIR `Statement` or `Terminator`) into a
|
||||
@ -164,10 +113,6 @@ impl CoverageSpan {
|
||||
/// execution
|
||||
/// * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures)
|
||||
struct CoverageSpansGenerator<'a> {
|
||||
/// A `Span` covering the function body of the MIR (typically from left curly brace to right
|
||||
/// curly brace).
|
||||
body_span: Span,
|
||||
|
||||
/// The BasicCoverageBlock Control Flow Graph (BCB CFG).
|
||||
basic_coverage_blocks: &'a CoverageGraph,
|
||||
|
||||
@ -244,7 +189,6 @@ impl<'a> CoverageSpansGenerator<'a> {
|
||||
);
|
||||
|
||||
let coverage_spans = Self {
|
||||
body_span: hir_info.body_span,
|
||||
basic_coverage_blocks,
|
||||
sorted_spans_iter: sorted_spans.into_iter(),
|
||||
some_curr: None,
|
||||
@ -266,7 +210,6 @@ impl<'a> CoverageSpansGenerator<'a> {
|
||||
// span-processing steps don't make sense yet.
|
||||
if self.some_prev.is_none() {
|
||||
debug!(" initial span");
|
||||
self.maybe_push_macro_name_span();
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -278,7 +221,6 @@ impl<'a> CoverageSpansGenerator<'a> {
|
||||
debug!(" same bcb (and neither is a closure), merge with prev={prev:?}");
|
||||
let prev = self.take_prev();
|
||||
self.curr_mut().merge_from(&prev);
|
||||
self.maybe_push_macro_name_span();
|
||||
// Note that curr.span may now differ from curr_original_span
|
||||
} else if prev.span.hi() <= curr.span.lo() {
|
||||
debug!(
|
||||
@ -286,7 +228,6 @@ impl<'a> CoverageSpansGenerator<'a> {
|
||||
);
|
||||
let prev = self.take_prev();
|
||||
self.refined_spans.push(prev);
|
||||
self.maybe_push_macro_name_span();
|
||||
} else if prev.is_closure {
|
||||
// drop any equal or overlapping span (`curr`) and keep `prev` to test again in the
|
||||
// next iter
|
||||
@ -297,35 +238,11 @@ impl<'a> CoverageSpansGenerator<'a> {
|
||||
} else if curr.is_closure {
|
||||
self.carve_out_span_for_closure();
|
||||
} else if self.prev_original_span == curr.span {
|
||||
// Note that this compares the new (`curr`) span to `prev_original_span`.
|
||||
// In this branch, the actual span byte range of `prev_original_span` is not
|
||||
// important. What is important is knowing whether the new `curr` span was
|
||||
// **originally** the same as the original span of `prev()`. The original spans
|
||||
// reflect their original sort order, and for equal spans, conveys a partial
|
||||
// ordering based on CFG dominator priority.
|
||||
if prev.is_macro_expansion() && curr.is_macro_expansion() {
|
||||
// Macros that expand to include branching (such as
|
||||
// `assert_eq!()`, `assert_ne!()`, `info!()`, `debug!()`, or
|
||||
// `trace!()`) typically generate callee spans with identical
|
||||
// ranges (typically the full span of the macro) for all
|
||||
// `BasicBlocks`. This makes it impossible to distinguish
|
||||
// the condition (`if val1 != val2`) from the optional
|
||||
// branched statements (such as the call to `panic!()` on
|
||||
// assert failure). In this case it is better (or less
|
||||
// worse) to drop the optional branch bcbs and keep the
|
||||
// non-conditional statements, to count when reached.
|
||||
debug!(
|
||||
" curr and prev are part of a macro expansion, and curr has the same span \
|
||||
as prev, but is in a different bcb. Drop curr and keep prev for next iter. \
|
||||
prev={prev:?}",
|
||||
);
|
||||
self.take_curr(); // Discards curr.
|
||||
} else {
|
||||
// `prev` and `curr` have the same span, or would have had the
|
||||
// same span before `prev` was modified by other spans.
|
||||
self.update_pending_dups();
|
||||
}
|
||||
} else {
|
||||
self.cutoff_prev_at_overlapping_curr();
|
||||
self.maybe_push_macro_name_span();
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,41 +277,6 @@ impl<'a> CoverageSpansGenerator<'a> {
|
||||
self.refined_spans
|
||||
}
|
||||
|
||||
/// If `curr` is part of a new macro expansion, carve out and push a separate
|
||||
/// span that ends just after the macro name and its subsequent `!`.
|
||||
fn maybe_push_macro_name_span(&mut self) {
|
||||
let curr = self.curr();
|
||||
|
||||
let Some(visible_macro) = curr.visible_macro(self.body_span) else { return };
|
||||
if let Some(prev) = &self.some_prev
|
||||
&& prev.expn_span.eq_ctxt(curr.expn_span)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The split point is relative to `curr_original_span`,
|
||||
// because `curr.span` may have been merged with preceding spans.
|
||||
let split_point_after_macro_bang = self.curr_original_span.lo()
|
||||
+ BytePos(visible_macro.as_str().len() as u32)
|
||||
+ BytePos(1); // add 1 for the `!`
|
||||
debug_assert!(split_point_after_macro_bang <= curr.span.hi());
|
||||
if split_point_after_macro_bang > curr.span.hi() {
|
||||
// Something is wrong with the macro name span;
|
||||
// return now to avoid emitting malformed mappings (e.g. #117788).
|
||||
return;
|
||||
}
|
||||
|
||||
let mut macro_name_cov = curr.clone();
|
||||
macro_name_cov.span = macro_name_cov.span.with_hi(split_point_after_macro_bang);
|
||||
self.curr_mut().span = curr.span.with_lo(split_point_after_macro_bang);
|
||||
|
||||
debug!(
|
||||
" and curr starts a new macro expansion, so add a new span just for \
|
||||
the macro `{visible_macro}!`, new span={macro_name_cov:?}",
|
||||
);
|
||||
self.refined_spans.push(macro_name_cov);
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn curr(&self) -> &CoverageSpan {
|
||||
self.some_curr.as_ref().unwrap_or_else(|| bug!("some_curr is None (curr)"))
|
||||
|
@ -1,11 +1,14 @@
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_middle::mir::{
|
||||
self, AggregateKind, FakeReadCause, Rvalue, Statement, StatementKind, Terminator,
|
||||
TerminatorKind,
|
||||
};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
|
||||
|
||||
use crate::coverage::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph};
|
||||
use crate::coverage::graph::{
|
||||
BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
|
||||
};
|
||||
use crate::coverage::spans::CoverageSpan;
|
||||
use crate::coverage::ExtractedHirInfo;
|
||||
|
||||
@ -15,26 +18,29 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
|
||||
basic_coverage_blocks: &CoverageGraph,
|
||||
) -> Vec<CoverageSpan> {
|
||||
let &ExtractedHirInfo { is_async_fn, fn_sig_span, body_span, .. } = hir_info;
|
||||
|
||||
let mut initial_spans = vec![SpanFromMir::for_fn_sig(fn_sig_span)];
|
||||
|
||||
if is_async_fn {
|
||||
// An async function desugars into a function that returns a future,
|
||||
// with the user code wrapped in a closure. Any spans in the desugared
|
||||
// outer function will be unhelpful, so just produce a single span
|
||||
// associating the function signature with its entry BCB.
|
||||
return vec![CoverageSpan::for_fn_sig(fn_sig_span)];
|
||||
}
|
||||
|
||||
let mut initial_spans = Vec::with_capacity(mir_body.basic_blocks.len() * 2);
|
||||
// outer function will be unhelpful, so just keep the signature span
|
||||
// and ignore all of the spans in the MIR body.
|
||||
} else {
|
||||
for (bcb, bcb_data) in basic_coverage_blocks.iter_enumerated() {
|
||||
initial_spans.extend(bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data));
|
||||
}
|
||||
|
||||
if initial_spans.is_empty() {
|
||||
// This can happen if, for example, the function is unreachable (contains only a
|
||||
// `BasicBlock`(s) with an `Unreachable` terminator).
|
||||
return initial_spans;
|
||||
// If no spans were extracted from the body, discard the signature span.
|
||||
// FIXME: This preserves existing behavior; consider getting rid of it.
|
||||
if initial_spans.len() == 1 {
|
||||
initial_spans.clear();
|
||||
}
|
||||
}
|
||||
|
||||
initial_spans.push(CoverageSpan::for_fn_sig(fn_sig_span));
|
||||
initial_spans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb));
|
||||
remove_unwanted_macro_spans(&mut initial_spans);
|
||||
split_visible_macro_spans(&mut initial_spans);
|
||||
|
||||
initial_spans.sort_by(|a, b| {
|
||||
// First sort by span start.
|
||||
@ -53,7 +59,62 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
|
||||
.then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse())
|
||||
});
|
||||
|
||||
initial_spans
|
||||
initial_spans.into_iter().map(SpanFromMir::into_coverage_span).collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
/// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate
|
||||
/// multiple condition/consequent blocks that have the span of the whole macro
|
||||
/// invocation, which is unhelpful. Keeping only the first such span seems to
|
||||
/// give better mappings, so remove the others.
|
||||
///
|
||||
/// (The input spans should be sorted in BCB dominator order, so that the
|
||||
/// retained "first" span is likely to dominate the others.)
|
||||
fn remove_unwanted_macro_spans(initial_spans: &mut Vec<SpanFromMir>) {
|
||||
let mut seen_macro_spans = FxHashSet::default();
|
||||
initial_spans.retain(|covspan| {
|
||||
// Ignore (retain) closure spans and non-macro-expansion spans.
|
||||
if covspan.is_closure || covspan.visible_macro.is_none() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Retain only the first macro-expanded covspan with this span.
|
||||
seen_macro_spans.insert(covspan.span)
|
||||
});
|
||||
}
|
||||
|
||||
/// When a span corresponds to a macro invocation that is visible from the
|
||||
/// function body, split it into two parts. The first part covers just the
|
||||
/// macro name plus `!`, and the second part covers the rest of the macro
|
||||
/// invocation. This seems to give better results for code that uses macros.
|
||||
fn split_visible_macro_spans(initial_spans: &mut Vec<SpanFromMir>) {
|
||||
let mut extra_spans = vec![];
|
||||
|
||||
initial_spans.retain(|covspan| {
|
||||
if covspan.is_closure {
|
||||
return true;
|
||||
}
|
||||
|
||||
let Some(visible_macro) = covspan.visible_macro else { return true };
|
||||
|
||||
let split_len = visible_macro.as_str().len() as u32 + 1;
|
||||
let (before, after) = covspan.span.split_at(split_len);
|
||||
if !covspan.span.contains(before) || !covspan.span.contains(after) {
|
||||
// Something is unexpectedly wrong with the split point.
|
||||
// The debug assertion in `split_at` will have already caught this,
|
||||
// but in release builds it's safer to do nothing and maybe get a
|
||||
// bug report for unexpected coverage, rather than risk an ICE.
|
||||
return true;
|
||||
}
|
||||
|
||||
assert!(!covspan.is_closure);
|
||||
extra_spans.push(SpanFromMir::new(before, covspan.visible_macro, covspan.bcb, false));
|
||||
extra_spans.push(SpanFromMir::new(after, covspan.visible_macro, covspan.bcb, false));
|
||||
false // Discard the original covspan that we just split.
|
||||
});
|
||||
|
||||
// The newly-split spans are added at the end, so any previous sorting
|
||||
// is not preserved.
|
||||
initial_spans.extend(extra_spans);
|
||||
}
|
||||
|
||||
// Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of
|
||||
@ -66,22 +127,24 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(
|
||||
body_span: Span,
|
||||
bcb: BasicCoverageBlock,
|
||||
bcb_data: &'a BasicCoverageBlockData,
|
||||
) -> impl Iterator<Item = CoverageSpan> + Captures<'a> + Captures<'tcx> {
|
||||
) -> impl Iterator<Item = SpanFromMir> + Captures<'a> + Captures<'tcx> {
|
||||
bcb_data.basic_blocks.iter().flat_map(move |&bb| {
|
||||
let data = &mir_body[bb];
|
||||
|
||||
let statement_spans = data.statements.iter().filter_map(move |statement| {
|
||||
let expn_span = filtered_statement_span(statement)?;
|
||||
let span = unexpand_into_body_span(expn_span, body_span)?;
|
||||
let (span, visible_macro) =
|
||||
unexpand_into_body_span_with_visible_macro(expn_span, body_span)?;
|
||||
|
||||
Some(CoverageSpan::new(span, expn_span, bcb, is_closure_or_coroutine(statement)))
|
||||
Some(SpanFromMir::new(span, visible_macro, bcb, is_closure_or_coroutine(statement)))
|
||||
});
|
||||
|
||||
let terminator_span = Some(data.terminator()).into_iter().filter_map(move |terminator| {
|
||||
let expn_span = filtered_terminator_span(terminator)?;
|
||||
let span = unexpand_into_body_span(expn_span, body_span)?;
|
||||
let (span, visible_macro) =
|
||||
unexpand_into_body_span_with_visible_macro(expn_span, body_span)?;
|
||||
|
||||
Some(CoverageSpan::new(span, expn_span, bcb, false))
|
||||
Some(SpanFromMir::new(span, visible_macro, bcb, false))
|
||||
});
|
||||
|
||||
statement_spans.chain(terminator_span)
|
||||
@ -202,7 +265,83 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
|
||||
///
|
||||
/// [^1]Expansions result from Rust syntax including macros, syntactic sugar,
|
||||
/// etc.).
|
||||
#[inline]
|
||||
fn unexpand_into_body_span(span: Span, body_span: Span) -> Option<Span> {
|
||||
span.find_ancestor_inside_same_ctxt(body_span)
|
||||
fn unexpand_into_body_span_with_visible_macro(
|
||||
original_span: Span,
|
||||
body_span: Span,
|
||||
) -> Option<(Span, Option<Symbol>)> {
|
||||
let (span, prev) = unexpand_into_body_span_with_prev(original_span, body_span)?;
|
||||
|
||||
let visible_macro = prev
|
||||
.map(|prev| match prev.ctxt().outer_expn_data().kind {
|
||||
ExpnKind::Macro(MacroKind::Bang, name) => Some(name),
|
||||
_ => None,
|
||||
})
|
||||
.flatten();
|
||||
|
||||
Some((span, visible_macro))
|
||||
}
|
||||
|
||||
/// Walks through the expansion ancestors of `original_span` to find a span that
|
||||
/// is contained in `body_span` and has the same [`SyntaxContext`] as `body_span`.
|
||||
/// The ancestor that was traversed just before the matching span (if any) is
|
||||
/// also returned.
|
||||
///
|
||||
/// For example, a return value of `Some((ancestor, Some(prev))` means that:
|
||||
/// - `ancestor == original_span.find_ancestor_inside_same_ctxt(body_span)`
|
||||
/// - `ancestor == prev.parent_callsite()`
|
||||
///
|
||||
/// [`SyntaxContext`]: rustc_span::SyntaxContext
|
||||
fn unexpand_into_body_span_with_prev(
|
||||
original_span: Span,
|
||||
body_span: Span,
|
||||
) -> Option<(Span, Option<Span>)> {
|
||||
let mut prev = None;
|
||||
let mut curr = original_span;
|
||||
|
||||
while !body_span.contains(curr) || !curr.eq_ctxt(body_span) {
|
||||
prev = Some(curr);
|
||||
curr = curr.parent_callsite()?;
|
||||
}
|
||||
|
||||
debug_assert_eq!(Some(curr), original_span.find_ancestor_in_same_ctxt(body_span));
|
||||
if let Some(prev) = prev {
|
||||
debug_assert_eq!(Some(curr), prev.parent_callsite());
|
||||
}
|
||||
|
||||
Some((curr, prev))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SpanFromMir {
|
||||
/// A span that has been extracted from MIR and then "un-expanded" back to
|
||||
/// within the current function's `body_span`. After various intermediate
|
||||
/// processing steps, this span is emitted as part of the final coverage
|
||||
/// mappings.
|
||||
///
|
||||
/// With the exception of `fn_sig_span`, this should always be contained
|
||||
/// within `body_span`.
|
||||
span: Span,
|
||||
visible_macro: Option<Symbol>,
|
||||
bcb: BasicCoverageBlock,
|
||||
is_closure: bool,
|
||||
}
|
||||
|
||||
impl SpanFromMir {
|
||||
fn for_fn_sig(fn_sig_span: Span) -> Self {
|
||||
Self::new(fn_sig_span, None, START_BCB, false)
|
||||
}
|
||||
|
||||
fn new(
|
||||
span: Span,
|
||||
visible_macro: Option<Symbol>,
|
||||
bcb: BasicCoverageBlock,
|
||||
is_closure: bool,
|
||||
) -> Self {
|
||||
Self { span, visible_macro, bcb, is_closure }
|
||||
}
|
||||
|
||||
fn into_coverage_span(self) -> CoverageSpan {
|
||||
let Self { span, visible_macro: _, bcb, is_closure } = self;
|
||||
CoverageSpan::new(span, bcb, is_closure)
|
||||
}
|
||||
}
|
||||
|
@ -81,21 +81,18 @@ Number of file 0 mappings: 1
|
||||
- Code(Zero) at (prev + 171, 13) to (start + 2, 14)
|
||||
|
||||
Function name: closure::main::{closure#14}
|
||||
Raw bytes (36): 0x[01, 01, 03, 05, 0a, 01, 05, 01, 05, 05, 03, b2, 01, 0d, 00, 15, 01, 01, 11, 01, 1b, 05, 01, 1e, 00, 25, 0a, 00, 2f, 00, 33, 03, 01, 0d, 00, 0e]
|
||||
Raw bytes (29): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, b2, 01, 0d, 02, 1b, 05, 02, 1e, 00, 25, 02, 00, 2f, 00, 33, 07, 01, 0d, 00, 0e]
|
||||
Number of files: 1
|
||||
- file 0 => global file 1
|
||||
Number of expressions: 3
|
||||
- expression 0 operands: lhs = Counter(1), rhs = Expression(2, Sub)
|
||||
- expression 1 operands: lhs = Counter(0), rhs = Counter(1)
|
||||
- expression 2 operands: lhs = Counter(0), rhs = Counter(1)
|
||||
Number of file 0 mappings: 5
|
||||
- Code(Expression(0, Add)) at (prev + 178, 13) to (start + 0, 21)
|
||||
= (c1 + (c0 - c1))
|
||||
- Code(Counter(0)) at (prev + 1, 17) to (start + 1, 27)
|
||||
- Code(Counter(1)) at (prev + 1, 30) to (start + 0, 37)
|
||||
- Code(Expression(2, Sub)) at (prev + 0, 47) to (start + 0, 51)
|
||||
Number of expressions: 2
|
||||
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
|
||||
- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
|
||||
Number of file 0 mappings: 4
|
||||
- Code(Counter(0)) at (prev + 178, 13) to (start + 2, 27)
|
||||
- Code(Counter(1)) at (prev + 2, 30) to (start + 0, 37)
|
||||
- Code(Expression(0, Sub)) at (prev + 0, 47) to (start + 0, 51)
|
||||
= (c0 - c1)
|
||||
- Code(Expression(0, Add)) at (prev + 1, 13) to (start + 0, 14)
|
||||
- Code(Expression(1, Add)) at (prev + 1, 13) to (start + 0, 14)
|
||||
= (c1 + (c0 - c1))
|
||||
|
||||
Function name: closure::main::{closure#15}
|
||||
@ -118,21 +115,18 @@ Number of file 0 mappings: 6
|
||||
= (c1 + (c0 - c1))
|
||||
|
||||
Function name: closure::main::{closure#16}
|
||||
Raw bytes (36): 0x[01, 01, 03, 05, 0a, 01, 05, 01, 05, 05, 03, c4, 01, 0d, 00, 15, 01, 01, 11, 01, 1b, 05, 01, 1e, 00, 25, 0a, 00, 2f, 00, 33, 03, 01, 0d, 00, 0e]
|
||||
Raw bytes (29): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, c4, 01, 0d, 02, 1b, 05, 02, 1e, 00, 25, 02, 00, 2f, 00, 33, 07, 01, 0d, 00, 0e]
|
||||
Number of files: 1
|
||||
- file 0 => global file 1
|
||||
Number of expressions: 3
|
||||
- expression 0 operands: lhs = Counter(1), rhs = Expression(2, Sub)
|
||||
- expression 1 operands: lhs = Counter(0), rhs = Counter(1)
|
||||
- expression 2 operands: lhs = Counter(0), rhs = Counter(1)
|
||||
Number of file 0 mappings: 5
|
||||
- Code(Expression(0, Add)) at (prev + 196, 13) to (start + 0, 21)
|
||||
= (c1 + (c0 - c1))
|
||||
- Code(Counter(0)) at (prev + 1, 17) to (start + 1, 27)
|
||||
- Code(Counter(1)) at (prev + 1, 30) to (start + 0, 37)
|
||||
- Code(Expression(2, Sub)) at (prev + 0, 47) to (start + 0, 51)
|
||||
Number of expressions: 2
|
||||
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
|
||||
- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub)
|
||||
Number of file 0 mappings: 4
|
||||
- Code(Counter(0)) at (prev + 196, 13) to (start + 2, 27)
|
||||
- Code(Counter(1)) at (prev + 2, 30) to (start + 0, 37)
|
||||
- Code(Expression(0, Sub)) at (prev + 0, 47) to (start + 0, 51)
|
||||
= (c0 - c1)
|
||||
- Code(Expression(0, Add)) at (prev + 1, 13) to (start + 0, 14)
|
||||
- Code(Expression(1, Add)) at (prev + 1, 13) to (start + 0, 14)
|
||||
= (c1 + (c0 - c1))
|
||||
|
||||
Function name: closure::main::{closure#17}
|
||||
|
@ -31,14 +31,14 @@ Number of file 0 mappings: 2
|
||||
- Code(Counter(0)) at (prev + 7, 6) to (start + 2, 2)
|
||||
|
||||
Function name: inline_dead::main::{closure#0}
|
||||
Raw bytes (23): 0x[01, 01, 02, 00, 06, 01, 00, 03, 01, 07, 17, 00, 18, 00, 02, 0d, 00, 0e, 03, 02, 05, 00, 06]
|
||||
Raw bytes (23): 0x[01, 01, 02, 00, 06, 01, 00, 03, 01, 07, 17, 01, 16, 00, 02, 0d, 00, 0e, 03, 02, 05, 00, 06]
|
||||
Number of files: 1
|
||||
- file 0 => global file 1
|
||||
Number of expressions: 2
|
||||
- expression 0 operands: lhs = Zero, rhs = Expression(1, Sub)
|
||||
- expression 1 operands: lhs = Counter(0), rhs = Zero
|
||||
Number of file 0 mappings: 3
|
||||
- Code(Counter(0)) at (prev + 7, 23) to (start + 0, 24)
|
||||
- Code(Counter(0)) at (prev + 7, 23) to (start + 1, 22)
|
||||
- Code(Zero) at (prev + 2, 13) to (start + 0, 14)
|
||||
- Code(Expression(0, Add)) at (prev + 2, 5) to (start + 0, 6)
|
||||
= (Zero + (c0 - Zero))
|
||||
|
@ -5,7 +5,7 @@
|
||||
LL| 1| println!("{}", live::<false>());
|
||||
LL| 1|
|
||||
LL| 1| let f = |x: bool| {
|
||||
LL| | debug_assert!(
|
||||
LL| 1| debug_assert!(
|
||||
LL| 0| x
|
||||
LL| | );
|
||||
LL| 1| };
|
||||
|
Loading…
Reference in New Issue
Block a user