Skip to content

Commit 7ad5281

Browse files
Rollup merge of #144877 - Zalathar:coverage-various, r=lcnr
coverage: Various small cleanups This PR is a collection of small coverage-related changes that I accumulated while working towards other coverage improvements. Each change should hopefully be fairly straightforward.
2 parents 1724af9 + b37c214 commit 7ad5281

File tree

12 files changed

+77
-271
lines changed

12 files changed

+77
-271
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
use std::assert_matches::assert_matches;
12
use std::sync::Arc;
23

34
use itertools::Itertools;
45
use rustc_abi::Align;
56
use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods};
67
use rustc_data_structures::fx::FxIndexMap;
78
use rustc_index::IndexVec;
9+
use rustc_macros::TryFromU32;
810
use rustc_middle::ty::TyCtxt;
911
use rustc_session::RemapFileNameExt;
1012
use rustc_session::config::RemapPathScopeComponents;
@@ -20,6 +22,23 @@ mod covfun;
2022
mod spans;
2123
mod unused;
2224

25+
/// Version number that will be included the `__llvm_covmap` section header.
26+
/// Corresponds to LLVM's `llvm::coverage::CovMapVersion` (in `CoverageMapping.h`),
27+
/// or at least the subset that we know and care about.
28+
///
29+
/// Note that version `n` is encoded as `(n-1)`.
30+
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, TryFromU32)]
31+
enum CovmapVersion {
32+
/// Used by LLVM 18 onwards.
33+
Version7 = 6,
34+
}
35+
36+
impl CovmapVersion {
37+
fn to_u32(self) -> u32 {
38+
self as u32
39+
}
40+
}
41+
2342
/// Generates and exports the coverage map, which is embedded in special
2443
/// linker sections in the final binary.
2544
///
@@ -29,19 +48,13 @@ pub(crate) fn finalize(cx: &mut CodegenCx<'_, '_>) {
2948
let tcx = cx.tcx;
3049

3150
// Ensure that LLVM is using a version of the coverage mapping format that
32-
// agrees with our Rust-side code. Expected versions (encoded as n-1) are:
33-
// - `CovMapVersion::Version7` (6) used by LLVM 18-19
34-
let covmap_version = {
35-
let llvm_covmap_version = llvm_cov::mapping_version();
36-
let expected_versions = 6..=6;
37-
assert!(
38-
expected_versions.contains(&llvm_covmap_version),
39-
"Coverage mapping version exposed by `llvm-wrapper` is out of sync; \
40-
expected {expected_versions:?} but was {llvm_covmap_version}"
41-
);
42-
// This is the version number that we will embed in the covmap section:
43-
llvm_covmap_version
44-
};
51+
// agrees with our Rust-side code. Expected versions are:
52+
// - `Version7` (6) used by LLVM 18 onwards.
53+
let covmap_version =
54+
CovmapVersion::try_from(llvm_cov::mapping_version()).unwrap_or_else(|raw_version: u32| {
55+
panic!("unknown coverage mapping version reported by `llvm-wrapper`: {raw_version}")
56+
});
57+
assert_matches!(covmap_version, CovmapVersion::Version7);
4558

4659
debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name());
4760

@@ -201,7 +214,11 @@ impl VirtualFileMapping {
201214
/// Generates the contents of the covmap record for this CGU, which mostly
202215
/// consists of a header and a list of filenames. The record is then stored
203216
/// as a global variable in the `__llvm_covmap` section.
204-
fn generate_covmap_record<'ll>(cx: &mut CodegenCx<'ll, '_>, version: u32, filenames_buffer: &[u8]) {
217+
fn generate_covmap_record<'ll>(
218+
cx: &mut CodegenCx<'ll, '_>,
219+
version: CovmapVersion,
220+
filenames_buffer: &[u8],
221+
) {
205222
// A covmap record consists of four target-endian u32 values, followed by
206223
// the encoded filenames table. Two of the header fields are unused in
207224
// modern versions of the LLVM coverage mapping format, and are always 0.
@@ -212,7 +229,7 @@ fn generate_covmap_record<'ll>(cx: &mut CodegenCx<'ll, '_>, version: u32, filena
212229
cx.const_u32(0), // (unused)
213230
cx.const_u32(filenames_buffer.len() as u32),
214231
cx.const_u32(0), // (unused)
215-
cx.const_u32(version),
232+
cx.const_u32(version.to_u32()),
216233
],
217234
/* packed */ false,
218235
);

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ use crate::llvm;
2727
/// the final record that will be embedded in the `__llvm_covfun` section.
2828
#[derive(Debug)]
2929
pub(crate) struct CovfunRecord<'tcx> {
30+
/// Not used directly, but helpful in debug messages.
31+
_instance: Instance<'tcx>,
32+
3033
mangled_function_name: &'tcx str,
3134
source_hash: u64,
3235
is_used: bool,
@@ -55,6 +58,7 @@ pub(crate) fn prepare_covfun_record<'tcx>(
5558
let expressions = prepare_expressions(ids_info);
5659

5760
let mut covfun = CovfunRecord {
61+
_instance: instance,
5862
mangled_function_name: tcx.symbol_name(instance).name,
5963
source_hash: if is_used { fn_cov_info.function_source_hash } else { 0 },
6064
is_used,
@@ -102,11 +106,21 @@ fn fill_region_tables<'tcx>(
102106
ids_info: &'tcx CoverageIdsInfo,
103107
covfun: &mut CovfunRecord<'tcx>,
104108
) {
109+
// If this function is unused, replace all counters with zero.
110+
let counter_for_bcb = |bcb: BasicCoverageBlock| -> ffi::Counter {
111+
let term = if covfun.is_used {
112+
ids_info.term_for_bcb[bcb].expect("every BCB in a mapping was given a term")
113+
} else {
114+
CovTerm::Zero
115+
};
116+
ffi::Counter::from_term(term)
117+
};
118+
105119
// Currently a function's mappings must all be in the same file, so use the
106120
// first mapping's span to determine the file.
107121
let source_map = tcx.sess.source_map();
108122
let Some(first_span) = (try { fn_cov_info.mappings.first()?.span }) else {
109-
debug_assert!(false, "function has no mappings: {:?}", covfun.mangled_function_name);
123+
debug_assert!(false, "function has no mappings: {covfun:?}");
110124
return;
111125
};
112126
let source_file = source_map.lookup_source_file(first_span.lo());
@@ -117,7 +131,7 @@ fn fill_region_tables<'tcx>(
117131
// codegen needs to handle that gracefully to avoid #133606.
118132
// It's hard for tests to trigger this organically, so instead we set
119133
// `-Zcoverage-options=discard-all-spans-in-codegen` to force it to occur.
120-
let discard_all = tcx.sess.coverage_discard_all_spans_in_codegen();
134+
let discard_all = tcx.sess.coverage_options().discard_all_spans_in_codegen;
121135
let make_coords = |span: Span| {
122136
if discard_all { None } else { spans::make_coords(source_map, &source_file, span) }
123137
};
@@ -133,16 +147,6 @@ fn fill_region_tables<'tcx>(
133147
// For each counter/region pair in this function+file, convert it to a
134148
// form suitable for FFI.
135149
for &Mapping { ref kind, span } in &fn_cov_info.mappings {
136-
// If this function is unused, replace all counters with zero.
137-
let counter_for_bcb = |bcb: BasicCoverageBlock| -> ffi::Counter {
138-
let term = if covfun.is_used {
139-
ids_info.term_for_bcb[bcb].expect("every BCB in a mapping was given a term")
140-
} else {
141-
CovTerm::Zero
142-
};
143-
ffi::Counter::from_term(term)
144-
};
145-
146150
let Some(coords) = make_coords(span) else { continue };
147151
let cov_span = coords.make_coverage_span(local_file_id);
148152

@@ -184,6 +188,7 @@ pub(crate) fn generate_covfun_record<'tcx>(
184188
covfun: &CovfunRecord<'tcx>,
185189
) {
186190
let &CovfunRecord {
191+
_instance,
187192
mangled_function_name,
188193
source_hash,
189194
is_used,

compiler/rustc_interface/src/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -778,8 +778,8 @@ fn test_unstable_options_tracking_hash() {
778778
coverage_options,
779779
CoverageOptions {
780780
level: CoverageLevel::Mcdc,
781-
no_mir_spans: true,
782-
discard_all_spans_in_codegen: true
781+
// (don't collapse test-only options onto the same line)
782+
discard_all_spans_in_codegen: true,
783783
}
784784
);
785785
tracked!(crate_attr, vec!["abc".to_string()]);

compiler/rustc_mir_transform/src/coverage/mappings.rs

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_middle::ty::TyCtxt;
1010
use rustc_span::Span;
1111

1212
use crate::coverage::ExtractedHirInfo;
13-
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
13+
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
1414
use crate::coverage::spans::extract_refined_covspans;
1515
use crate::coverage::unexpand::unexpand_into_body_span;
1616
use crate::errors::MCDCExceedsTestVectorLimit;
@@ -82,22 +82,8 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
8282
let mut mcdc_degraded_branches = vec![];
8383
let mut mcdc_mappings = vec![];
8484

85-
if hir_info.is_async_fn || tcx.sess.coverage_no_mir_spans() {
86-
// An async function desugars into a function that returns a future,
87-
// with the user code wrapped in a closure. Any spans in the desugared
88-
// outer function will be unhelpful, so just keep the signature span
89-
// and ignore all of the spans in the MIR body.
90-
//
91-
// When debugging flag `-Zcoverage-options=no-mir-spans` is set, we need
92-
// to give the same treatment to _all_ functions, because `llvm-cov`
93-
// seems to ignore functions that don't have any ordinary code spans.
94-
if let Some(span) = hir_info.fn_sig_span {
95-
code_mappings.push(CodeMapping { span, bcb: START_BCB });
96-
}
97-
} else {
98-
// Extract coverage spans from MIR statements/terminators as normal.
99-
extract_refined_covspans(tcx, mir_body, hir_info, graph, &mut code_mappings);
100-
}
85+
// Extract ordinary code mappings from MIR statement/terminator spans.
86+
extract_refined_covspans(tcx, mir_body, hir_info, graph, &mut code_mappings);
10187

10288
branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, graph));
10389

compiler/rustc_mir_transform/src/coverage/spans.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use rustc_data_structures::fx::FxHashSet;
22
use rustc_middle::mir;
3+
use rustc_middle::mir::coverage::START_BCB;
34
use rustc_middle::ty::TyCtxt;
45
use rustc_span::source_map::SourceMap;
56
use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span};
@@ -16,8 +17,19 @@ pub(super) fn extract_refined_covspans<'tcx>(
1617
mir_body: &mir::Body<'tcx>,
1718
hir_info: &ExtractedHirInfo,
1819
graph: &CoverageGraph,
19-
code_mappings: &mut impl Extend<mappings::CodeMapping>,
20+
code_mappings: &mut Vec<mappings::CodeMapping>,
2021
) {
22+
if hir_info.is_async_fn {
23+
// An async function desugars into a function that returns a future,
24+
// with the user code wrapped in a closure. Any spans in the desugared
25+
// outer function will be unhelpful, so just keep the signature span
26+
// and ignore all of the spans in the MIR body.
27+
if let Some(span) = hir_info.fn_sig_span {
28+
code_mappings.push(mappings::CodeMapping { span, bcb: START_BCB });
29+
}
30+
return;
31+
}
32+
2133
let &ExtractedHirInfo { body_span, .. } = hir_info;
2234

2335
let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph);

compiler/rustc_session/src/config.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,14 +182,7 @@ pub enum InstrumentCoverage {
182182
pub struct CoverageOptions {
183183
pub level: CoverageLevel,
184184

185-
/// `-Zcoverage-options=no-mir-spans`: Don't extract block coverage spans
186-
/// from MIR statements/terminators, making it easier to inspect/debug
187-
/// branch and MC/DC coverage mappings.
188-
///
189-
/// For internal debugging only. If other code changes would make it hard
190-
/// to keep supporting this flag, remove it.
191-
pub no_mir_spans: bool,
192-
185+
/// **(internal test-only flag)**
193186
/// `-Zcoverage-options=discard-all-spans-in-codegen`: During codegen,
194187
/// discard all coverage spans as though they were invalid. Needed by
195188
/// regression tests for #133606, because we don't have an easy way to

compiler/rustc_session/src/options.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -755,8 +755,7 @@ mod desc {
755755
pub(crate) const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of();
756756
pub(crate) const parse_dump_mono_stats: &str = "`markdown` (default) or `json`";
757757
pub(crate) const parse_instrument_coverage: &str = parse_bool;
758-
pub(crate) const parse_coverage_options: &str =
759-
"`block` | `branch` | `condition` | `mcdc` | `no-mir-spans`";
758+
pub(crate) const parse_coverage_options: &str = "`block` | `branch` | `condition` | `mcdc`";
760759
pub(crate) const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`";
761760
pub(crate) const parse_unpretty: &str = "`string` or `string=string`";
762761
pub(crate) const parse_treat_err_as_bug: &str = "either no value or a non-negative number";
@@ -1460,7 +1459,6 @@ pub mod parse {
14601459
"branch" => slot.level = CoverageLevel::Branch,
14611460
"condition" => slot.level = CoverageLevel::Condition,
14621461
"mcdc" => slot.level = CoverageLevel::Mcdc,
1463-
"no-mir-spans" => slot.no_mir_spans = true,
14641462
"discard-all-spans-in-codegen" => slot.discard_all_spans_in_codegen = true,
14651463
_ => return false,
14661464
}

compiler/rustc_session/src/session.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ use rustc_target::spec::{
3939
use crate::code_stats::CodeStats;
4040
pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
4141
use crate::config::{
42-
self, CoverageLevel, CrateType, DebugInfo, ErrorOutputType, FunctionReturn, Input,
43-
InstrumentCoverage, OptLevel, OutFileName, OutputType, RemapPathScopeComponents,
42+
self, CoverageLevel, CoverageOptions, CrateType, DebugInfo, ErrorOutputType, FunctionReturn,
43+
Input, InstrumentCoverage, OptLevel, OutFileName, OutputType, RemapPathScopeComponents,
4444
SwitchWithOptPath,
4545
};
4646
use crate::filesearch::FileSearch;
@@ -359,14 +359,11 @@ impl Session {
359359
&& self.opts.unstable_opts.coverage_options.level >= CoverageLevel::Mcdc
360360
}
361361

362-
/// True if `-Zcoverage-options=no-mir-spans` was passed.
363-
pub fn coverage_no_mir_spans(&self) -> bool {
364-
self.opts.unstable_opts.coverage_options.no_mir_spans
365-
}
366-
367-
/// True if `-Zcoverage-options=discard-all-spans-in-codegen` was passed.
368-
pub fn coverage_discard_all_spans_in_codegen(&self) -> bool {
369-
self.opts.unstable_opts.coverage_options.discard_all_spans_in_codegen
362+
/// Provides direct access to the `CoverageOptions` struct, so that
363+
/// individual flags for debugging/testing coverage instrumetation don't
364+
/// need separate accessors.
365+
pub fn coverage_options(&self) -> &CoverageOptions {
366+
&self.opts.unstable_opts.coverage_options
370367
}
371368

372369
pub fn is_sanitizer_cfi_enabled(&self) -> bool {

tests/coverage/branch/no-mir-spans.cov-map

Lines changed: 0 additions & 63 deletions
This file was deleted.

0 commit comments

Comments
 (0)