Skip to content

Commit 81b59a7

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 c9fb0d4 commit 81b59a7

File tree

8 files changed

+110
-23
lines changed

8 files changed

+110
-23
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,6 @@ impl CounterMappingRegion {
181181
}
182182
}
183183

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

compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,23 @@ pub struct Expression {
1414
region: Option<CodeRegion>,
1515
}
1616

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

@@ -58,6 +76,7 @@ impl<'tcx> FunctionCoverage<'tcx> {
5876
is_used,
5977
counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize),
6078
expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize),
79+
branches: Vec::new(),
6180
unreachable_regions: Vec::new(),
6281
}
6382
}
@@ -84,6 +103,11 @@ impl<'tcx> FunctionCoverage<'tcx> {
84103
}
85104
}
86105

106+
/// Adds a branch region using the two provided true/false operands
107+
pub fn add_branch_counter(&mut self, true_: Operand, false_: Operand, region: CodeRegion) {
108+
self.branches.push(CoverageBranch { true_, false_, region })
109+
}
110+
87111
/// Both counters and "counter expressions" (or simply, "expressions") can be operands in other
88112
/// expressions. These are tracked as separate variants of `Operand`, so there is no ambiguity
89113
/// between operands that are counter IDs and operands that are expression IDs.
@@ -185,7 +209,7 @@ impl<'tcx> FunctionCoverage<'tcx> {
185209
/// `CoverageMapGenerator` will create `CounterMappingRegion`s.
186210
pub fn get_expressions_and_counter_regions(
187211
&self,
188-
) -> (Vec<CounterExpression>, Vec<(Counter, &CodeRegion)>) {
212+
) -> (Vec<CounterExpression>, Vec<CoverageCounterAndRegion<'_>>) {
189213
assert!(
190214
self.source_hash != 0 || !self.is_used,
191215
"No counters provided the source_hash for used function: {:?}",
@@ -201,26 +225,43 @@ impl<'tcx> FunctionCoverage<'tcx> {
201225
let counter_regions = self.counters.iter_enumerated().filter_map(|(index, entry)| {
202226
// Option::map() will return None to filter out missing counters. This may happen
203227
// if, for example, a MIR-instrumented counter is removed during an optimization.
204-
entry.as_ref().map(|region| (Counter::counter_value_reference(index), region))
228+
entry.as_ref().map(|region| CoverageCounterAndRegion {
229+
kind: CoverageCounterKind::Counter(Counter::counter_value_reference(index)),
230+
region,
231+
})
205232
});
206233

207234
// Find all of the expression IDs that weren't optimized out AND have
208235
// an attached code region, and return the corresponding mapping as a
209236
// counter/region pair.
210237
let expression_regions =
211238
self.expressions.iter_enumerated().filter_map(|(id, expression)| {
212-
let code_region = expression.as_ref()?.region.as_ref()?;
213-
Some((Counter::expression(id), code_region))
239+
let region = expression.as_ref()?.region.as_ref()?;
240+
Some(CoverageCounterAndRegion {
241+
kind: CoverageCounterKind::Counter(Counter::expression(id)),
242+
region,
243+
})
214244
});
215-
let unreachable_regions =
216-
self.unreachable_regions.iter().map(|region| (Counter::ZERO, region));
245+
246+
let unreachable_regions = self.unreachable_regions.iter().map(|region| {
247+
CoverageCounterAndRegion { kind: CoverageCounterKind::Counter(Counter::ZERO), region }
248+
});
249+
250+
let branch_regions = self.branches.iter().map(|branch| CoverageCounterAndRegion {
251+
kind: CoverageCounterKind::Branch {
252+
true_counter: Counter::from_operand(branch.true_),
253+
false_counter: Counter::from_operand(branch.false_),
254+
},
255+
region: &branch.region,
256+
});
217257

218258
let mut all_regions: Vec<_> = expression_regions.collect();
219259
all_regions.extend(counter_regions);
220260
all_regions.extend(unreachable_regions);
261+
all_regions.extend(branch_regions);
221262

222263
// make sure all the regions are sorted
223-
all_regions.sort_unstable_by_key(|(_counter, region)| *region);
264+
all_regions.sort_unstable_by_key(|coverage_region| coverage_region.region);
224265

225266
(counter_expressions, all_regions)
226267
}

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

Lines changed: 35 additions & 13 deletions
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
@@ -146,7 +148,7 @@ impl CoverageMapGenerator {
146148
fn write_coverage_mapping(
147149
&mut self,
148150
expressions: Vec<CounterExpression>,
149-
counter_regions: Vec<(Counter, &CodeRegion)>,
151+
counter_regions: Vec<CoverageCounterAndRegion<'_>>,
150152
coverage_mapping_buffer: &RustString,
151153
) {
152154
if counter_regions.is_empty() {
@@ -158,12 +160,13 @@ impl CoverageMapGenerator {
158160
let mut current_file_name = None;
159161
let mut current_file_id = 0;
160162

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

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

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

Lines changed: 8 additions & 0 deletions
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, code_region);
154154
}
155+
CoverageKind::Branch { true_, false_ } => {
156+
let code_region = code_region.expect("branch regions always have code regions");
157+
debug!(
158+
"adding branch to coverage_map: instance={:?}, {:?} / {:?}; region: {:?}",
159+
instance, true_, false_, code_region,
160+
);
161+
func_coverage.add_branch_counter(true_, false_, code_region);
162+
}
155163
CoverageKind::Unreachable => {
156164
let code_region =
157165
code_region.expect("unreachable regions always have code regions");

compiler/rustc_middle/src/mir/coverage.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ pub enum CoverageKind {
8383
op: Op,
8484
rhs: Operand,
8585
},
86+
Branch {
87+
true_: Operand,
88+
false_: Operand,
89+
},
8690
Unreachable,
8791
}
8892

@@ -92,6 +96,7 @@ impl CoverageKind {
9296
match *self {
9397
Counter { id, .. } => Operand::Counter(id),
9498
Expression { id, .. } => Operand::Expression(id),
99+
Branch { .. } => bug!("Branch coverage cannot be part of an expression"),
95100
Unreachable => bug!("Unreachable coverage cannot be part of an expression"),
96101
}
97102
}
@@ -117,6 +122,9 @@ impl Debug for CoverageKind {
117122
},
118123
rhs,
119124
),
125+
Branch { true_, false_ } => {
126+
write!(fmt, "Branch: {true_:?} / {false_:?}")
127+
}
120128
Unreachable => write!(fmt, "Unreachable"),
121129
}
122130
}

compiler/rustc_mir_transform/src/coverage/debug.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,9 @@ impl DebugCounters {
299299
CoverageKind::Expression { .. } => {
300300
format!("Expression({})", self.format_counter_kind(counter_kind))
301301
}
302+
CoverageKind::Branch { true_, false_ } => {
303+
format!("Branch({} / {})", self.format_operand(true_), self.format_operand(false_))
304+
}
302305
CoverageKind::Unreachable { .. } => "Unreachable".to_owned(),
303306
}
304307
}

compiler/rustc_smir/src/rustc_smir/mod.rs

Lines changed: 4 additions & 0 deletions
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_, false_ } => stable_mir::mir::CoverageKind::Branch {
495+
true_: opaque(true_),
496+
false_: opaque(false_),
497+
},
494498
CoverageKind::Unreachable => stable_mir::mir::CoverageKind::Unreachable,
495499
}
496500
}

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

Lines changed: 4 additions & 0 deletions
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_: ExpressionOperandId,
189+
false_: ExpressionOperandId,
190+
},
187191
Unreachable,
188192
}
189193

0 commit comments

Comments
 (0)