Skip to content

Commit fc52c47

Browse files
committed
Add a new CoverageKind::Branch to MIR
This adds a couple intermediate types, and threads branch coverage from MIR through codegen.
1 parent 64ebdc2 commit fc52c47

File tree

7 files changed

+122
-24
lines changed

7 files changed

+122
-24
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs

-3
Original file line numberDiff line numberDiff line change
@@ -183,9 +183,6 @@ impl CounterMappingRegion {
183183
}
184184
}
185185

186-
// This function might be used in the future; the LLVM API is still evolving, as is coverage
187-
// support.
188-
#[allow(dead_code)]
189186
pub(crate) fn branch_region(
190187
counter: Counter,
191188
false_counter: Counter,

compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs

+66-8
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,23 @@ pub struct Expression {
1616
region: Option<CodeRegion>,
1717
}
1818

19+
pub struct CoverageCounterAndRegion<'a> {
20+
pub(crate) kind: CoverageCounterKind,
21+
pub(crate) region: &'a CodeRegion,
22+
}
23+
24+
pub enum CoverageCounterKind {
25+
Counter(Counter),
26+
Branch { true_counter: Counter, false_counter: Counter },
27+
}
28+
29+
#[derive(Debug)]
30+
struct CoverageBranch {
31+
true_op: Operand,
32+
false_op: Operand,
33+
region: CodeRegion,
34+
}
35+
1936
/// Collects all of the coverage regions associated with (a) injected counters, (b) counter
2037
/// expressions (additions or subtraction), and (c) unreachable regions (always counted as zero),
2138
/// for a given Function. This struct also stores the `function_source_hash`,
@@ -34,6 +51,7 @@ pub struct FunctionCoverage<'tcx> {
3451
is_used: bool,
3552
counters: IndexVec<CounterId, Option<CodeRegion>>,
3653
expressions: IndexVec<ExpressionId, Option<Expression>>,
54+
branches: Vec<CoverageBranch>,
3755
unreachable_regions: Vec<CodeRegion>,
3856
}
3957

@@ -60,6 +78,7 @@ impl<'tcx> FunctionCoverage<'tcx> {
6078
is_used,
6179
counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize),
6280
expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize),
81+
branches: Vec::new(),
6382
unreachable_regions: Vec::new(),
6483
}
6584
}
@@ -86,6 +105,11 @@ impl<'tcx> FunctionCoverage<'tcx> {
86105
}
87106
}
88107

108+
/// Adds a branch region using the two provided true/false operands
109+
pub fn add_branch_counter(&mut self, true_op: Operand, false_op: Operand, region: CodeRegion) {
110+
self.branches.push(CoverageBranch { true_op, false_op, region })
111+
}
112+
89113
/// Both counters and "counter expressions" (or simply, "expressions") can be operands in other
90114
/// expressions. These are tracked as separate variants of `Operand`, so there is no ambiguity
91115
/// between operands that are counter IDs and operands that are expression IDs.
@@ -139,7 +163,7 @@ impl<'tcx> FunctionCoverage<'tcx> {
139163
/// `CoverageMapGenerator` will create `CounterMappingRegion`s.
140164
pub fn get_expressions_and_counter_regions(
141165
&self,
142-
) -> (Vec<CounterExpression>, Vec<(Counter, &CodeRegion)>) {
166+
) -> (Vec<CounterExpression>, Vec<CoverageCounterAndRegion<'_>>) {
143167
assert!(
144168
self.source_hash != 0 || !self.is_used,
145169
"No counters provided the source_hash for used function: {:?}",
@@ -153,22 +177,25 @@ impl<'tcx> FunctionCoverage<'tcx> {
153177
let mut collected_counter_regions = expression_regions;
154178
collected_counter_regions.extend(counter_regions);
155179
collected_counter_regions.extend(unreachable_regions);
156-
collected_counter_regions.sort_unstable_by_key(|(_counter, region)| *region);
180+
collected_counter_regions.sort_unstable_by_key(|counter| counter.region);
157181

158182
(counter_expressions, collected_counter_regions)
159183
}
160184

161-
fn counter_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)> {
185+
fn counter_regions(&self) -> impl Iterator<Item = CoverageCounterAndRegion<'_>> {
162186
self.counters.iter_enumerated().filter_map(|(index, entry)| {
163187
// Option::map() will return None to filter out missing counters. This may happen
164188
// if, for example, a MIR-instrumented counter is removed during an optimization.
165-
entry.as_ref().map(|region| (Counter::counter_value_reference(index), region))
189+
entry.as_ref().map(|region| CoverageCounterAndRegion {
190+
kind: CoverageCounterKind::Counter(Counter::counter_value_reference(index)),
191+
region,
192+
})
166193
})
167194
}
168195

169196
fn expressions_with_regions(
170197
&self,
171-
) -> (Vec<CounterExpression>, Vec<(Counter, &CodeRegion)>) {
198+
) -> (Vec<CounterExpression>, Vec<CoverageCounterAndRegion<'_>>) {
172199
let mut counter_expressions = Vec::with_capacity(self.expressions.len());
173200
let mut expression_regions = Vec::with_capacity(self.expressions.len());
174201
let mut new_indexes = IndexVec::from_elem_n(None, self.expressions.len());
@@ -291,7 +318,12 @@ impl<'tcx> FunctionCoverage<'tcx> {
291318
counter_expressions.push(expression);
292319
new_indexes[original_index] = Some(mapped_expression_index);
293320
if let Some(region) = optional_region {
294-
expression_regions.push((Counter::expression(mapped_expression_index), region));
321+
expression_regions.push(CoverageCounterAndRegion {
322+
kind: CoverageCounterKind::Counter(Counter::expression(
323+
mapped_expression_index,
324+
)),
325+
region,
326+
});
295327
}
296328
} else {
297329
bug!(
@@ -305,10 +337,36 @@ impl<'tcx> FunctionCoverage<'tcx> {
305337
);
306338
}
307339
}
340+
341+
for branch in &self.branches {
342+
if let Some(Some((true_counter, false_counter))) =
343+
id_to_counter(&new_indexes, branch.true_op).map(|true_counter| {
344+
id_to_counter(&new_indexes, branch.false_op)
345+
.map(|false_counter| (true_counter, false_counter))
346+
})
347+
{
348+
expression_regions.push(CoverageCounterAndRegion {
349+
kind: CoverageCounterKind::Branch { true_counter, false_counter },
350+
region: &branch.region,
351+
});
352+
} else {
353+
bug!(
354+
"branch has one or more missing operands \
355+
true={:?}, false={:?}, region={:?}",
356+
branch.true_op,
357+
branch.false_op,
358+
branch.region,
359+
);
360+
}
361+
}
362+
308363
(counter_expressions, expression_regions)
309364
}
310365

311-
fn unreachable_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)> {
312-
self.unreachable_regions.iter().map(|region| (Counter::zero(), region))
366+
fn unreachable_regions(&self) -> impl Iterator<Item = CoverageCounterAndRegion<'_>> {
367+
self.unreachable_regions.iter().map(|region| CoverageCounterAndRegion {
368+
kind: CoverageCounterKind::Counter(Counter::zero()),
369+
region,
370+
})
313371
}
314372
}

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

+35-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::common::CodegenCx;
22
use crate::coverageinfo;
3-
use crate::coverageinfo::ffi::{Counter, CounterExpression, CounterMappingRegion};
3+
use crate::coverageinfo::ffi::{CounterExpression, CounterMappingRegion};
44
use crate::llvm;
55

66
use rustc_codegen_ssa::traits::ConstMethods;
@@ -14,6 +14,8 @@ use rustc_middle::mir::coverage::CodeRegion;
1414
use rustc_middle::ty::TyCtxt;
1515
use rustc_span::Symbol;
1616

17+
use super::map_data::{CoverageCounterAndRegion, CoverageCounterKind};
18+
1719
/// Generates and exports the Coverage Map.
1820
///
1921
/// Rust Coverage Map generation supports LLVM Coverage Mapping Format version
@@ -144,7 +146,7 @@ impl CoverageMapGenerator {
144146
fn write_coverage_mapping(
145147
&mut self,
146148
expressions: Vec<CounterExpression>,
147-
counter_regions: Vec<(Counter, &CodeRegion)>,
149+
counter_regions: Vec<CoverageCounterAndRegion<'_>>,
148150
coverage_mapping_buffer: &RustString,
149151
) {
150152
if counter_regions.is_empty() {
@@ -156,12 +158,13 @@ impl CoverageMapGenerator {
156158
let mut current_file_name = None;
157159
let mut current_file_id = 0;
158160

159-
// Convert the list of (Counter, CodeRegion) pairs to an array of `CounterMappingRegion`, sorted
161+
// Convert the list of `CoverageCounterAndRegion` to an array of `CounterMappingRegion`, sorted
160162
// by filename and position. Capture any new files to compute the `CounterMappingRegion`s
161163
// `file_id` (indexing files referenced by the current function), and construct the
162164
// function-specific `virtual_file_mapping` from `file_id` to its index in the module's
163165
// `filenames` array.
164-
for (counter, region) in counter_regions {
166+
for counter_region in counter_regions {
167+
let region = counter_region.region;
165168
let CodeRegion { file_name, start_line, start_col, end_line, end_col } = *region;
166169
let same_file = current_file_name.is_some_and(|p| p == file_name);
167170
if !same_file {
@@ -173,15 +176,34 @@ impl CoverageMapGenerator {
173176
let (filenames_index, _) = self.filenames.insert_full(file_name);
174177
virtual_file_mapping.push(filenames_index as u32);
175178
}
176-
debug!("Adding counter {:?} to map for {:?}", counter, region);
177-
mapping_regions.push(CounterMappingRegion::code_region(
178-
counter,
179-
current_file_id,
180-
start_line,
181-
start_col,
182-
end_line,
183-
end_col,
184-
));
179+
match counter_region.kind {
180+
CoverageCounterKind::Counter(counter) => {
181+
debug!("Adding counter {:?} to map for {:?}", counter, region);
182+
mapping_regions.push(CounterMappingRegion::code_region(
183+
counter,
184+
current_file_id,
185+
start_line,
186+
start_col,
187+
end_line,
188+
end_col,
189+
));
190+
}
191+
CoverageCounterKind::Branch { true_counter, false_counter } => {
192+
debug!(
193+
"Adding branch ({:?} / {:?}) to map for {:?}",
194+
true_counter, false_counter, region
195+
);
196+
mapping_regions.push(CounterMappingRegion::branch_region(
197+
true_counter,
198+
false_counter,
199+
current_file_id,
200+
start_line,
201+
start_col,
202+
end_line,
203+
end_col,
204+
));
205+
}
206+
}
185207
}
186208

187209
// Encode and append the current function's coverage mapping data

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,14 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
152152
);
153153
func_coverage.add_counter_expression(id, lhs, op, rhs, region);
154154
}
155+
CoverageKind::Branch { true_op, false_op } => {
156+
let region = region.expect("branch regions always have code regions");
157+
debug!(
158+
"adding branch to coverage_map: instance={:?}, {:?} / {:?}; region: {:?}",
159+
instance, true_op, false_op, region,
160+
);
161+
func_coverage.add_branch_counter(true_op, false_op, region);
162+
}
155163
CoverageKind::Unreachable => {
156164
let region = region.expect("unreachable regions always have code regions");
157165
debug!(

compiler/rustc_middle/src/mir/coverage.rs

+5
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ pub enum CoverageKind {
9393
op: Op,
9494
rhs: Operand,
9595
},
96+
Branch {
97+
true_op: Operand,
98+
false_op: Operand,
99+
},
96100
Unreachable,
97101
}
98102

@@ -112,6 +116,7 @@ impl Debug for CoverageKind {
112116
},
113117
rhs,
114118
),
119+
Branch { true_op, false_op } => write!(fmt, "Branch: {true_op:?} / {false_op:?}"),
115120
Unreachable => write!(fmt, "Unreachable"),
116121
}
117122
}

compiler/rustc_smir/src/rustc_smir/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,10 @@ impl<'tcx> Stable<'tcx> for mir::coverage::CoverageKind {
491491
rhs: opaque(rhs),
492492
}
493493
}
494+
CoverageKind::Branch { true_op, false_op } => stable_mir::mir::CoverageKind::Branch {
495+
true_op: opaque(true_op),
496+
false_op: opaque(false_op),
497+
},
494498
CoverageKind::Unreachable => stable_mir::mir::CoverageKind::Unreachable,
495499
}
496500
}

compiler/rustc_smir/src/stable_mir/mir/body.rs

+4
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,10 @@ pub enum CoverageKind {
184184
op: Op,
185185
rhs: ExpressionOperandId,
186186
},
187+
Branch {
188+
true_op: ExpressionOperandId,
189+
false_op: ExpressionOperandId,
190+
},
187191
Unreachable,
188192
}
189193

0 commit comments

Comments
 (0)