Skip to content

Commit a1dfd24

Browse files
committed
Auto merge of #77080 - richkadel:llvm-coverage-counters-2, r=tmandry
Working branch-level code coverage Add a generalized implementation for computing branch-level coverage spans. This iteration resolves some of the challenges I had identified a few weeks ago. I've tried to implement a solution that is general enough to work for a lot of different graphs/patterns. It's encouraging to see the results on fairly large and complex crates seem to meet my expectations. This may be a "functionally complete" implementation. Except for bug fixes or edge cases I haven't run into yet, the next and essentially final step, I think, is to replace some Counters with CounterExpressions (where their counter values can be computed by adding or subtracting other counters/expressions). Examples of branch-level coverage support enabled in this PR: * https://github.com/richkadel/rust/blob/llvm-coverage-counters-2/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/expected_show_coverage.coverage_of_drop_trait.txt * https://github.com/richkadel/rust/blob/llvm-coverage-counters-2/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/expected_show_coverage.coverage_of_if.txt * https://github.com/richkadel/rust/blob/llvm-coverage-counters-2/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/expected_show_coverage.coverage_of_if_else.txt * https://github.com/richkadel/rust/blob/llvm-coverage-counters-2/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/expected_show_coverage.coverage_of_simple_loop.txt * https://github.com/richkadel/rust/blob/llvm-coverage-counters-2/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/expected_show_coverage.coverage_of_simple_match.txt * ... _and others in the same directory_ Examples of coverage analysis results (MIR spanview files) used to inject counters in the right `BasicBlocks`: * https://htmlpreview.github.io/?https://github.com/richkadel/rust/blob/llvm-coverage-counters-2/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/expected_mir_dump.coverage_of_drop_trait/coverage_of_drop_trait.main.-------.InstrumentCoverage.0.html * https://htmlpreview.github.io/?https://github.com/richkadel/rust/blob/llvm-coverage-counters-2/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/expected_mir_dump.coverage_of_if/coverage_of_if.main.-------.InstrumentCoverage.0.html * https://htmlpreview.github.io/?https://github.com/richkadel/rust/blob/llvm-coverage-counters-2/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/expected_mir_dump.coverage_of_if_else/coverage_of_if_else.main.-------.InstrumentCoverage.0.html * https://htmlpreview.github.io/?https://github.com/richkadel/rust/blob/llvm-coverage-counters-2/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/expected_mir_dump.coverage_of_simple_loop/coverage_of_simple_loop.main.-------.InstrumentCoverage.0.html * https://htmlpreview.github.io/?https://github.com/richkadel/rust/blob/llvm-coverage-counters-2/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/expected_mir_dump.coverage_of_simple_match/coverage_of_simple_match.main.-------.InstrumentCoverage.0.html * ... _and others in the same directory_ Here is some sample coverage output after compiling a few real-world crates with the new branch-level coverage features: <img width="801" alt="Screen Shot 2020-09-25 at 1 03 11 PM" src="https://user-images.githubusercontent.com/3827298/94316848-fd882c00-ff39-11ea-9cff-0402d3abd1e7.png"> <img width="721" alt="Screen Shot 2020-09-25 at 1 00 36 PM" src="https://user-images.githubusercontent.com/3827298/94316886-11cc2900-ff3a-11ea-9d03-80b26c8a5173.png"> <img width="889" alt="Screen Shot 2020-09-25 at 12 54 57 PM" src="https://user-images.githubusercontent.com/3827298/94316900-18f33700-ff3a-11ea-8a80-58f67d84b8de.png"> r? `@tmandry` FYI: `@wesleywiser`
2 parents ea7e131 + 6f62766 commit a1dfd24

File tree

151 files changed

+19001
-1878
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

151 files changed

+19001
-1878
lines changed

Cargo.lock

+2-1
Original file line numberDiff line numberDiff line change
@@ -2938,8 +2938,9 @@ dependencies = [
29382938

29392939
[[package]]
29402940
name = "rust-demangler"
2941-
version = "0.0.0"
2941+
version = "0.0.1"
29422942
dependencies = [
2943+
"regex",
29432944
"rustc-demangle",
29442945
]
29452946

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

+1
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ impl CoverageMapGenerator {
126126
let (filenames_index, _) = self.filenames.insert_full(c_filename);
127127
virtual_file_mapping.push(filenames_index as u32);
128128
}
129+
debug!("Adding counter {:?} to map for {:?}", counter, region,);
129130
mapping_regions.push(CounterMappingRegion::code_region(
130131
counter,
131132
current_file_id,

compiler/rustc_codegen_ssa/src/coverageinfo/map.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ impl FunctionCoverage {
143143
let id_to_counter =
144144
|new_indexes: &IndexVec<InjectedExpressionIndex, MappedExpressionIndex>,
145145
id: ExpressionOperandId| {
146-
if id.index() < self.counters.len() {
146+
if id == ExpressionOperandId::ZERO {
147+
Some(Counter::zero())
148+
} else if id.index() < self.counters.len() {
147149
let index = CounterValueReference::from(id.index());
148150
self.counters
149151
.get(index)
@@ -179,14 +181,19 @@ impl FunctionCoverage {
179181
// been assigned a `new_index`.
180182
let mapped_expression_index =
181183
MappedExpressionIndex::from(counter_expressions.len());
182-
counter_expressions.push(CounterExpression::new(
184+
let expression = CounterExpression::new(
183185
lhs_counter,
184186
match op {
185187
Op::Add => ExprKind::Add,
186188
Op::Subtract => ExprKind::Subtract,
187189
},
188190
rhs_counter,
189-
));
191+
);
192+
debug!(
193+
"Adding expression {:?} = {:?} at {:?}",
194+
mapped_expression_index, expression, region
195+
);
196+
counter_expressions.push(expression);
190197
new_indexes[original_index] = mapped_expression_index;
191198
expression_regions.push((Counter::expression(mapped_expression_index), region));
192199
}

compiler/rustc_data_structures/src/graph/dominators/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use super::iterate::reverse_post_order;
99
use super::ControlFlowGraph;
1010
use rustc_index::vec::{Idx, IndexVec};
1111
use std::borrow::BorrowMut;
12+
use std::cmp::Ordering;
1213

1314
#[cfg(test)]
1415
mod tests;
@@ -108,6 +109,14 @@ impl<Node: Idx> Dominators<Node> {
108109
// FIXME -- could be optimized by using post-order-rank
109110
self.dominators(node).any(|n| n == dom)
110111
}
112+
113+
/// Provide deterministic ordering of nodes such that, if any two nodes have a dominator
114+
/// relationship, the dominator will always precede the dominated. (The relative ordering
115+
/// of two unrelated nodes will also be consistent, but otherwise the order has no
116+
/// meaning.) This method cannot be used to determine if either Node dominates the other.
117+
pub fn rank_partial_cmp(&self, lhs: Node, rhs: Node) -> Option<Ordering> {
118+
self.post_order_rank[lhs].partial_cmp(&self.post_order_rank[rhs])
119+
}
111120
}
112121

113122
pub struct Iter<'dom, Node: Idx> {

compiler/rustc_middle/src/mir/coverage/mod.rs

+19
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,20 @@ rustc_index::newtype_index! {
1414
}
1515
}
1616

17+
impl ExpressionOperandId {
18+
/// An expression operand for a "zero counter", as described in the following references:
19+
///
20+
/// * https://github.com/rust-lang/llvm-project/blob/llvmorg-8.0.0/llvm/docs/CoverageMappingFormat.rst#counter
21+
/// * https://github.com/rust-lang/llvm-project/blob/llvmorg-8.0.0/llvm/docs/CoverageMappingFormat.rst#tag
22+
/// * https://github.com/rust-lang/llvm-project/blob/llvmorg-8.0.0/llvm/docs/CoverageMappingFormat.rst#counter-expressions
23+
///
24+
/// This operand can be used to count two or more separate code regions with a single counter,
25+
/// if they run sequentially with no branches, by injecting the `Counter` in a `BasicBlock` for
26+
/// one of the code regions, and inserting `CounterExpression`s ("add ZERO to the counter") in
27+
/// the coverage map for the other code regions.
28+
pub const ZERO: Self = Self::from_u32(0);
29+
}
30+
1731
rustc_index::newtype_index! {
1832
pub struct CounterValueReference {
1933
derive [HashStable]
@@ -22,6 +36,11 @@ rustc_index::newtype_index! {
2236
}
2337
}
2438

39+
impl CounterValueReference {
40+
// Counters start at 1 to reserve 0 for ExpressionOperandId::ZERO.
41+
pub const START: Self = Self::from_u32(1);
42+
}
43+
2544
rustc_index::newtype_index! {
2645
pub struct InjectedExpressionIndex {
2746
derive [HashStable]

compiler/rustc_middle/src/mir/visit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,7 @@ macro_rules! make_mir_visitor {
752752
}
753753

754754
fn super_coverage(&mut self,
755-
_kind: & $($mutability)? Coverage,
755+
_coverage: & $($mutability)? Coverage,
756756
_location: Location) {
757757
}
758758

0 commit comments

Comments
 (0)