Skip to content

Commit 6436892

Browse files
committed
Make CodeRegions optional for Counters too
I relized the "hack" with GapRegions was completely unnecessary. It is possible to inject counters (`llvm.instrprof.increment` intrinsic calls without corresponding code regions in the coverage map. An expression can still uses these counter values, solving the problem I thought I needed GapRegions for.
1 parent 736ddb9 commit 6436892

20 files changed

+123
-276
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -126,33 +126,15 @@ 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,);
130-
if start_line == end_line && start_col == end_col {
131-
// The MIR `InstrumentCoverage` pass generates empty spans _ONLY_ for `BasicBlocks`
132-
// that contribute to the counts of `CoverageKind::Expression`s, but don't represent
133-
// specific source code to count.
134-
//
135-
// Make empty spans `GapRegion`s so their region executions counts are still
136-
// available to any expressions that might reference them, but they don't affect
137-
// the line execution count (as long as the line has at least one other counter).
138-
mapping_regions.push(CounterMappingRegion::gap_region(
139-
counter,
140-
current_file_id,
141-
start_line,
142-
start_col,
143-
end_line,
144-
end_col,
145-
));
146-
} else {
147-
mapping_regions.push(CounterMappingRegion::code_region(
148-
counter,
149-
current_file_id,
150-
start_line,
151-
start_col,
152-
end_line,
153-
end_col,
154-
));
155-
}
129+
debug!("Adding counter {:?} to map for {:?}", counter, region);
130+
mapping_regions.push(CounterMappingRegion::code_region(
131+
counter,
132+
current_file_id,
133+
start_line,
134+
start_col,
135+
end_line,
136+
end_col,
137+
));
156138
}
157139

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

compiler/rustc_codegen_ssa/src/coverageinfo/map.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,12 +149,10 @@ impl FunctionCoverage {
149149
if id == ExpressionOperandId::ZERO {
150150
Some(Counter::zero())
151151
} else if id.index() < self.counters.len() {
152+
// Note: Some codegen-injected Counters may be only referenced by `Expression`s,
153+
// and may not have their own `CodeRegion`s,
152154
let index = CounterValueReference::from(id.index());
153-
self.counters
154-
.get(index)
155-
.expect("counter id is out of range")
156-
.as_ref()
157-
.map(|_| Counter::counter_value_reference(index))
155+
Some(Counter::counter_value_reference(index))
158156
} else {
159157
let index = self.expression_index(u32::from(id));
160158
self.expressions

compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,23 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
1010
let Coverage { kind, code_region } = coverage;
1111
match kind {
1212
CoverageKind::Counter { function_source_hash, id } => {
13-
bx.add_coverage_counter(
14-
self.instance,
15-
function_source_hash,
16-
id,
17-
code_region.expect("counters always have code regions"),
18-
);
19-
2013
let coverageinfo = bx.tcx().coverageinfo(self.instance.def_id());
2114

2215
let fn_name = bx.create_pgo_func_name_var(self.instance);
2316
let hash = bx.const_u64(function_source_hash);
2417
let num_counters = bx.const_u32(coverageinfo.num_counters);
25-
let id = bx.const_u32(u32::from(id));
18+
let index = bx.const_u32(u32::from(id));
2619
debug!(
2720
"codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})",
28-
fn_name, hash, num_counters, id,
21+
fn_name, hash, num_counters, index,
2922
);
30-
bx.instrprof_increment(fn_name, hash, num_counters, id);
23+
bx.instrprof_increment(fn_name, hash, num_counters, index);
24+
25+
if let Some(code_region) = code_region {
26+
bx.add_coverage_counter(self.instance, function_source_hash, id, code_region);
27+
}
28+
// Note: Some counters do not have code regions, but may still be referenced from
29+
// expressions.
3130
}
3231
CoverageKind::Expression { id, lhs, op, rhs } => {
3332
bx.add_coverage_counter_expression(self.instance, id, lhs, op, rhs, code_region);

compiler/rustc_mir/src/transform/instrument_coverage.rs

Lines changed: 36 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,12 +1244,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
12441244
} else {
12451245
bug!("Every BasicCoverageBlock should have a Counter or Expression");
12461246
};
1247-
debug!(
1248-
"Injecting {} at: {:?}:\n{}\n==========",
1249-
self.format_counter(&counter_kind),
1250-
span,
1251-
source_map.span_to_snippet(span).expect("Error getting source for span"),
1252-
);
12531247
if let Some(bcb_to_coverage_spans_with_counters) =
12541248
debug_bcb_to_coverage_spans_with_counters.as_mut()
12551249
{
@@ -1258,25 +1252,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
12581252
.or_insert_with(|| Vec::new())
12591253
.push((covspan.clone(), counter_kind.clone()));
12601254
}
1261-
let mut code_region = None;
1262-
if span.hi() == body_span.hi() {
1263-
// All functions execute a `Return`-terminated `BasicBlock`, regardless of how the
1264-
// function returns; but only some functions also _can_ return after a `Goto` block
1265-
// that ends on the closing brace of the function (with the `Return`). When this
1266-
// happens, the last character is counted 2 (or possibly more) times, when we know
1267-
// the function returned only once (of course). By giving all `Goto` terminators at
1268-
// the end of a function a `non-reportable` code region, they are still counted
1269-
// if appropriate, but they don't increment the line counter, as long as their is
1270-
// also a `Return` on that last line.
1271-
if let TerminatorKind::Goto { .. } = self.bcb_terminator(bcb).kind {
1272-
code_region =
1273-
Some(make_non_reportable_code_region(file_name, &source_file, span));
1274-
}
1275-
}
1276-
if code_region.is_none() {
1277-
code_region = Some(make_code_region(file_name, &source_file, span, body_span));
1255+
let some_code_region = if self.is_code_region_redundant(bcb, span, body_span) {
1256+
None
1257+
} else {
1258+
Some(make_code_region(file_name, &source_file, span, body_span))
12781259
};
1279-
self.inject_statement(counter_kind, self.bcb_last_bb(bcb), code_region.unwrap());
1260+
self.inject_statement(counter_kind, self.bcb_last_bb(bcb), some_code_region);
12801261
}
12811262

12821263
// The previous step looped through the `CoverageSpan`s and injected the counter from the
@@ -1423,18 +1404,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
14231404
target_bb
14241405
};
14251406

1426-
let span = self.mir_body[inject_to_bb].terminator().source_info.span;
1427-
debug!(
1428-
"make_non_reportable_code_region for {:?} at span={:?}, counter={}",
1429-
inject_to_bb,
1430-
span,
1431-
self.format_counter(&counter_kind)
1432-
);
1433-
self.inject_statement(
1434-
counter_kind,
1435-
inject_to_bb,
1436-
make_non_reportable_code_region(file_name, &source_file, span),
1437-
);
1407+
self.inject_statement(counter_kind, inject_to_bb, None);
14381408
}
14391409
CoverageKind::Expression { .. } => {
14401410
self.inject_intermediate_expression(counter_kind)
@@ -1583,6 +1553,28 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
15831553
}
15841554
}
15851555

1556+
fn is_code_region_redundant(
1557+
&self,
1558+
bcb: BasicCoverageBlock,
1559+
span: Span,
1560+
body_span: Span,
1561+
) -> bool {
1562+
if span.hi() == body_span.hi() {
1563+
// All functions execute a `Return`-terminated `BasicBlock`, regardless of how the
1564+
// function returns; but only some functions also _can_ return after a `Goto` block
1565+
// that ends on the closing brace of the function (with the `Return`). When this
1566+
// happens, the last character is counted 2 (or possibly more) times, when we know
1567+
// the function returned only once (of course). By giving all `Goto` terminators at
1568+
// the end of a function a `non-reportable` code region, they are still counted
1569+
// if appropriate, but they don't increment the line counter, as long as their is
1570+
// also a `Return` on that last line.
1571+
if let TerminatorKind::Goto { .. } = self.bcb_terminator(bcb).kind {
1572+
return true;
1573+
}
1574+
}
1575+
false
1576+
}
1577+
15861578
/// Traverse the BCB CFG and add either a `Counter` or `Expression` to ever BCB, to be
15871579
/// injected with `CoverageSpan`s. `Expressions` have no runtime overhead, so if a viable
15881580
/// expression (adding or subtracting two other counters or expressions) can compute the same
@@ -2139,16 +2131,19 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
21392131
&mut self,
21402132
counter_kind: CoverageKind,
21412133
bb: BasicBlock,
2142-
code_region: CodeRegion,
2134+
some_code_region: Option<CodeRegion>,
21432135
) {
2144-
debug!(" injecting statement {:?} covering {:?}", counter_kind, code_region);
2136+
debug!(
2137+
" injecting statement {:?} for {:?} at code region: {:?}",
2138+
counter_kind, bb, some_code_region
2139+
);
21452140
let data = &mut self.mir_body[bb];
21462141
let source_info = data.terminator().source_info;
21472142
let statement = Statement {
21482143
source_info,
21492144
kind: StatementKind::Coverage(box Coverage {
21502145
kind: counter_kind,
2151-
code_region: Some(code_region),
2146+
code_region: some_code_region,
21522147
}),
21532148
};
21542149
data.statements.push(statement);
@@ -2762,8 +2757,8 @@ fn filtered_terminator_span(terminator: &'a Terminator<'tcx>, body_span: Span) -
27622757
//
27632758
// However, in other cases, a visible `CoverageSpan` is not wanted, but the `Goto`
27642759
// block must still be counted (for example, to contribute its count to an `Expression`
2765-
// that reports the execution count for some other block). The code region for the `Goto`
2766-
// counters, in these cases, is created using `make_non_reportable_code_region()`.
2760+
// that reports the execution count for some other block). In these cases, the code region
2761+
// is set to `None`.
27672762
TerminatorKind::Goto { .. } => {
27682763
Some(function_source_span(terminator.source_info.span.shrink_to_hi(), body_span))
27692764
}
@@ -2788,35 +2783,6 @@ fn function_source_span(span: Span, body_span: Span) -> Span {
27882783
if body_span.contains(span) { span } else { body_span }
27892784
}
27902785

2791-
/// Make a non-reportable code region. (Used coverage-generated "edge counters", and for some
2792-
/// `Goto`-terminated blocks.)
2793-
///
2794-
/// Only the `Span`s `hi()` position is used, and an empty `Span` at that location is generated,
2795-
/// for coverage counting.
2796-
///
2797-
/// The coverage map generation phase (part of codegen) knows to report any `Counter` or
2798-
/// `Expression` with an empty span as what LLVM's Coverage Mapping Specification calls a
2799-
/// `GapRegion`, rather than a `CodeRegion` (used in all other cases). Counters associated with a
2800-
/// `GapRegion` still contribute their counter values to other expressions, but if the `GapRegion`s
2801-
/// line has any other `CodeRegion` covering the same line, the `GapRegion` will not be counted
2802-
/// toward the line execution count. This prevents double-counting line execution, which would
2803-
/// otherwise occur.
2804-
fn make_non_reportable_code_region(
2805-
file_name: Symbol,
2806-
source_file: &Lrc<SourceFile>,
2807-
span: Span,
2808-
) -> CodeRegion {
2809-
let (start_line, start_col) = source_file.lookup_file_pos(span.hi());
2810-
let (end_line, end_col) = (start_line, start_col);
2811-
CodeRegion {
2812-
file_name,
2813-
start_line: start_line as u32,
2814-
start_col: start_col.to_u32() + 1,
2815-
end_line: end_line as u32,
2816-
end_col: end_col.to_u32() + 1,
2817-
}
2818-
}
2819-
28202786
/// Convert the Span into its file name, start line and column, and end line and column
28212787
fn make_code_region(
28222788
file_name: Symbol,

src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
},
1818
"lines": {
1919
"count": 40,
20-
"covered": 29,
21-
"percent": 72.5
20+
"covered": 30,
21+
"percent": 75
2222
},
2323
"regions": {
2424
"count": 37,
@@ -42,8 +42,8 @@
4242
},
4343
"lines": {
4444
"count": 40,
45-
"covered": 29,
46-
"percent": 72.5
45+
"covered": 30,
46+
"percent": 75
4747
},
4848
"regions": {
4949
"count": 37,

src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.various_conditions.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
},
1818
"lines": {
1919
"count": 49,
20-
"covered": 24,
21-
"percent": 48.97959183673469
20+
"covered": 23,
21+
"percent": 46.93877551020408
2222
},
2323
"regions": {
2424
"count": 69,
@@ -42,8 +42,8 @@
4242
},
4343
"lines": {
4444
"count": 49,
45-
"covered": 24,
46-
"percent": 48.97959183673469
45+
"covered": 23,
46+
"percent": 46.93877551020408
4747
},
4848
"regions": {
4949
"count": 69,

src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.lazy_boolean.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
17| | =
1919
18| 1| a < b
2020
19| | ||
21-
20| 0| b < c
21+
20| 1| b < c
22+
^0
2223
21| | ;
2324
22| | let
2425
23| 1| somebool

src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.various_conditions.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@
2727
27| |
2828
28| 1| if countdown > 7 {
2929
29| 1| countdown -= 4;
30-
30| 1| } else if countdown > 2 {
31-
^0
30+
30| 0| } else if countdown > 2 {
3231
31| 0| if countdown < 1 || countdown > 5 || countdown != 9 {
3332
32| 0| countdown = 0;
3433
33| 0| }

0 commit comments

Comments
 (0)