Skip to content

Commit b95232d

Browse files
authored
Rollup merge of #132675 - Zalathar:empty-spans, r=jieyouxu
coverage: Restrict empty-span expansion to only cover `{` and `}` Coverage instrumentation has some tricky code for converting a coverage-relevant `Span` into a set of start/end line/byte-column coordinates that will be embedded in the CGU's coverage metadata. A big part of this complexity is special code for handling empty spans, which are expanded into non-empty spans (if possible) because LLVM's coverage reporter does not handle empty spans well. This PR simplifies that code by restricting it to only apply in two specific situations: when the character after the empty span is `{`, or the character before the empty span is `}`. (As an added benefit, this means that the expanded spans no longer extend awkwardly beyond the end of a physical line, which was common under the previous implementation.) Along the way, this PR also removes some unhelpful code for dealing with function source code spread across multiple files. Functions currently can't have coverage spans in multiple files, and if that ever changes (e.g. to properly support expansion regions) then this code will need to be completely overhauled anyway.
2 parents c22887b + 925dfc8 commit b95232d

Some content is hidden

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

57 files changed

+320
-305
lines changed

Diff for: compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId, SourceRegion};
22

3+
use crate::coverageinfo::mapgen::LocalFileId;
4+
35
/// Must match the layout of `LLVMRustCounterKind`.
46
#[derive(Copy, Clone, Debug)]
57
#[repr(C)]
@@ -137,8 +139,12 @@ pub(crate) struct CoverageSpan {
137139
}
138140

139141
impl CoverageSpan {
140-
pub(crate) fn from_source_region(file_id: u32, code_region: &SourceRegion) -> Self {
141-
let &SourceRegion { file_name: _, start_line, start_col, end_line, end_col } = code_region;
142+
pub(crate) fn from_source_region(
143+
local_file_id: LocalFileId,
144+
code_region: &SourceRegion,
145+
) -> Self {
146+
let file_id = local_file_id.as_u32();
147+
let &SourceRegion { start_line, start_col, end_line, end_col } = code_region;
142148
// Internally, LLVM uses the high bit of `end_col` to distinguish between
143149
// code regions and gap regions, so it can't be used by the column number.
144150
assert!(end_col & (1u32 << 31) == 0, "high bit of `end_col` must be unset: {end_col:#X}");

Diff for: compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use rustc_middle::mir::coverage::{
66
SourceRegion,
77
};
88
use rustc_middle::ty::Instance;
9-
use rustc_span::Symbol;
109
use tracing::{debug, instrument};
1110

1211
use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
@@ -180,7 +179,7 @@ impl<'tcx> FunctionCoverageCollector<'tcx> {
180179
}
181180

182181
pub(crate) struct FunctionCoverage<'tcx> {
183-
function_coverage_info: &'tcx FunctionCoverageInfo,
182+
pub(crate) function_coverage_info: &'tcx FunctionCoverageInfo,
184183
is_used: bool,
185184

186185
counters_seen: BitSet<CounterId>,
@@ -199,11 +198,6 @@ impl<'tcx> FunctionCoverage<'tcx> {
199198
if self.is_used { self.function_coverage_info.function_source_hash } else { 0 }
200199
}
201200

202-
/// Returns an iterator over all filenames used by this function's mappings.
203-
pub(crate) fn all_file_names(&self) -> impl Iterator<Item = Symbol> + Captures<'_> {
204-
self.function_coverage_info.mappings.iter().map(|mapping| mapping.source_region.file_name)
205-
}
206-
207201
/// Convert this function's coverage expression data into a form that can be
208202
/// passed through FFI to LLVM.
209203
pub(crate) fn counter_expressions(

Diff for: compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

+72-58
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ use rustc_index::IndexVec;
1212
use rustc_middle::mir::coverage::MappingKind;
1313
use rustc_middle::ty::{self, TyCtxt};
1414
use rustc_middle::{bug, mir};
15-
use rustc_span::Symbol;
15+
use rustc_session::RemapFileNameExt;
16+
use rustc_session::config::RemapPathScopeComponents;
1617
use rustc_span::def_id::DefIdSet;
18+
use rustc_span::{Span, Symbol};
1719
use rustc_target::spec::HasTargetSpec;
1820
use tracing::debug;
1921

@@ -70,8 +72,10 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
7072
.map(|(instance, function_coverage)| (instance, function_coverage.into_finished()))
7173
.collect::<Vec<_>>();
7274

73-
let all_file_names =
74-
function_coverage_entries.iter().flat_map(|(_, fn_cov)| fn_cov.all_file_names());
75+
let all_file_names = function_coverage_entries
76+
.iter()
77+
.map(|(_, fn_cov)| fn_cov.function_coverage_info.body_span)
78+
.map(|span| span_file_name(tcx, span));
7579
let global_file_table = GlobalFileTable::new(all_file_names);
7680

7781
// Encode all filenames referenced by coverage mappings in this CGU.
@@ -96,7 +100,7 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
96100
let is_used = function_coverage.is_used();
97101

98102
let coverage_mapping_buffer =
99-
encode_mappings_for_function(&global_file_table, &function_coverage);
103+
encode_mappings_for_function(tcx, &global_file_table, &function_coverage);
100104

101105
if coverage_mapping_buffer.is_empty() {
102106
if function_coverage.is_used() {
@@ -164,13 +168,13 @@ impl GlobalFileTable {
164168
Self { raw_file_table }
165169
}
166170

167-
fn global_file_id_for_file_name(&self, file_name: Symbol) -> u32 {
171+
fn global_file_id_for_file_name(&self, file_name: Symbol) -> GlobalFileId {
168172
let raw_id = self.raw_file_table.get_index_of(&file_name).unwrap_or_else(|| {
169173
bug!("file name not found in prepared global file table: {file_name}");
170174
});
171175
// The raw file table doesn't include an entry for the working dir
172176
// (which has ID 0), so add 1 to get the correct ID.
173-
(raw_id + 1) as u32
177+
GlobalFileId::from_usize(raw_id + 1)
174178
}
175179

176180
fn make_filenames_buffer(&self, tcx: TyCtxt<'_>) -> Vec<u8> {
@@ -196,36 +200,54 @@ impl GlobalFileTable {
196200
}
197201

198202
rustc_index::newtype_index! {
199-
struct LocalFileId {}
203+
/// An index into the CGU's overall list of file paths. The underlying paths
204+
/// will be embedded in the `__llvm_covmap` linker section.
205+
struct GlobalFileId {}
206+
}
207+
rustc_index::newtype_index! {
208+
/// An index into a function's list of global file IDs. That underlying list
209+
/// of local-to-global mappings will be embedded in the function's record in
210+
/// the `__llvm_covfun` linker section.
211+
pub(crate) struct LocalFileId {}
200212
}
201213

202214
/// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU)
203215
/// file IDs.
204216
#[derive(Default)]
205217
struct VirtualFileMapping {
206-
local_to_global: IndexVec<LocalFileId, u32>,
207-
global_to_local: FxIndexMap<u32, LocalFileId>,
218+
local_to_global: IndexVec<LocalFileId, GlobalFileId>,
219+
global_to_local: FxIndexMap<GlobalFileId, LocalFileId>,
208220
}
209221

210222
impl VirtualFileMapping {
211-
fn local_id_for_global(&mut self, global_file_id: u32) -> LocalFileId {
223+
fn local_id_for_global(&mut self, global_file_id: GlobalFileId) -> LocalFileId {
212224
*self
213225
.global_to_local
214226
.entry(global_file_id)
215227
.or_insert_with(|| self.local_to_global.push(global_file_id))
216228
}
217229

218230
fn into_vec(self) -> Vec<u32> {
219-
self.local_to_global.raw
231+
// This conversion should be optimized away to ~zero overhead.
232+
// In any case, it's probably not hot enough to worry about.
233+
self.local_to_global.into_iter().map(|global| global.as_u32()).collect()
220234
}
221235
}
222236

237+
fn span_file_name(tcx: TyCtxt<'_>, span: Span) -> Symbol {
238+
let source_file = tcx.sess.source_map().lookup_source_file(span.lo());
239+
let name =
240+
source_file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy();
241+
Symbol::intern(&name)
242+
}
243+
223244
/// Using the expressions and counter regions collected for a single function,
224245
/// generate the variable-sized payload of its corresponding `__llvm_covfun`
225246
/// entry. The payload is returned as a vector of bytes.
226247
///
227248
/// Newly-encountered filenames will be added to the global file table.
228249
fn encode_mappings_for_function(
250+
tcx: TyCtxt<'_>,
229251
global_file_table: &GlobalFileTable,
230252
function_coverage: &FunctionCoverage<'_>,
231253
) -> Vec<u8> {
@@ -242,53 +264,45 @@ fn encode_mappings_for_function(
242264
let mut mcdc_branch_regions = vec![];
243265
let mut mcdc_decision_regions = vec![];
244266

245-
// Group mappings into runs with the same filename, preserving the order
246-
// yielded by `FunctionCoverage`.
247-
// Prepare file IDs for each filename, and prepare the mapping data so that
248-
// we can pass it through FFI to LLVM.
249-
for (file_name, counter_regions_for_file) in
250-
&counter_regions.group_by(|(_, region)| region.file_name)
251-
{
252-
// Look up the global file ID for this filename.
253-
let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
254-
255-
// Associate that global file ID with a local file ID for this function.
256-
let local_file_id = virtual_file_mapping.local_id_for_global(global_file_id);
257-
debug!(" file id: {local_file_id:?} => global {global_file_id} = '{file_name:?}'");
258-
259-
// For each counter/region pair in this function+file, convert it to a
260-
// form suitable for FFI.
261-
for (mapping_kind, region) in counter_regions_for_file {
262-
debug!("Adding counter {mapping_kind:?} to map for {region:?}");
263-
let span = ffi::CoverageSpan::from_source_region(local_file_id.as_u32(), region);
264-
match mapping_kind {
265-
MappingKind::Code(term) => {
266-
code_regions
267-
.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) });
268-
}
269-
MappingKind::Branch { true_term, false_term } => {
270-
branch_regions.push(ffi::BranchRegion {
271-
span,
272-
true_counter: ffi::Counter::from_term(true_term),
273-
false_counter: ffi::Counter::from_term(false_term),
274-
});
275-
}
276-
MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => {
277-
mcdc_branch_regions.push(ffi::MCDCBranchRegion {
278-
span,
279-
true_counter: ffi::Counter::from_term(true_term),
280-
false_counter: ffi::Counter::from_term(false_term),
281-
mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params),
282-
});
283-
}
284-
MappingKind::MCDCDecision(mcdc_decision_params) => {
285-
mcdc_decision_regions.push(ffi::MCDCDecisionRegion {
286-
span,
287-
mcdc_decision_params: ffi::mcdc::DecisionParameters::from(
288-
mcdc_decision_params,
289-
),
290-
});
291-
}
267+
// Currently a function's mappings must all be in the same file as its body span.
268+
let file_name = span_file_name(tcx, function_coverage.function_coverage_info.body_span);
269+
270+
// Look up the global file ID for that filename.
271+
let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
272+
273+
// Associate that global file ID with a local file ID for this function.
274+
let local_file_id = virtual_file_mapping.local_id_for_global(global_file_id);
275+
debug!(" file id: {local_file_id:?} => {global_file_id:?} = '{file_name:?}'");
276+
277+
// For each counter/region pair in this function+file, convert it to a
278+
// form suitable for FFI.
279+
for (mapping_kind, region) in counter_regions {
280+
debug!("Adding counter {mapping_kind:?} to map for {region:?}");
281+
let span = ffi::CoverageSpan::from_source_region(local_file_id, region);
282+
match mapping_kind {
283+
MappingKind::Code(term) => {
284+
code_regions.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) });
285+
}
286+
MappingKind::Branch { true_term, false_term } => {
287+
branch_regions.push(ffi::BranchRegion {
288+
span,
289+
true_counter: ffi::Counter::from_term(true_term),
290+
false_counter: ffi::Counter::from_term(false_term),
291+
});
292+
}
293+
MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => {
294+
mcdc_branch_regions.push(ffi::MCDCBranchRegion {
295+
span,
296+
true_counter: ffi::Counter::from_term(true_term),
297+
false_counter: ffi::Counter::from_term(false_term),
298+
mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params),
299+
});
300+
}
301+
MappingKind::MCDCDecision(mcdc_decision_params) => {
302+
mcdc_decision_regions.push(ffi::MCDCDecisionRegion {
303+
span,
304+
mcdc_decision_params: ffi::mcdc::DecisionParameters::from(mcdc_decision_params),
305+
});
292306
}
293307
}
294308
}

Diff for: compiler/rustc_middle/src/mir/coverage.rs

+4-7
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::fmt::{self, Debug, Formatter};
44

55
use rustc_index::IndexVec;
66
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
7-
use rustc_span::{Span, Symbol};
7+
use rustc_span::Span;
88

99
rustc_index::newtype_index! {
1010
/// Used by [`CoverageKind::BlockMarker`] to mark blocks during THIR-to-MIR
@@ -158,7 +158,6 @@ impl Debug for CoverageKind {
158158
#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, Eq, PartialOrd, Ord)]
159159
#[derive(TypeFoldable, TypeVisitable)]
160160
pub struct SourceRegion {
161-
pub file_name: Symbol,
162161
pub start_line: u32,
163162
pub start_col: u32,
164163
pub end_line: u32,
@@ -167,11 +166,8 @@ pub struct SourceRegion {
167166

168167
impl Debug for SourceRegion {
169168
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
170-
write!(
171-
fmt,
172-
"{}:{}:{} - {}:{}",
173-
self.file_name, self.start_line, self.start_col, self.end_line, self.end_col
174-
)
169+
let &Self { start_line, start_col, end_line, end_col } = self;
170+
write!(fmt, "{start_line}:{start_col} - {end_line}:{end_col}")
175171
}
176172
}
177173

@@ -246,6 +242,7 @@ pub struct Mapping {
246242
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
247243
pub struct FunctionCoverageInfo {
248244
pub function_source_hash: u64,
245+
pub body_span: Span,
249246
pub num_counters: usize,
250247
pub mcdc_bitmap_bits: usize,
251248
pub expressions: IndexVec<ExpressionId, Expression>,

Diff for: compiler/rustc_middle/src/mir/pretty.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -596,8 +596,10 @@ fn write_function_coverage_info(
596596
function_coverage_info: &coverage::FunctionCoverageInfo,
597597
w: &mut dyn io::Write,
598598
) -> io::Result<()> {
599-
let coverage::FunctionCoverageInfo { expressions, mappings, .. } = function_coverage_info;
599+
let coverage::FunctionCoverageInfo { body_span, expressions, mappings, .. } =
600+
function_coverage_info;
600601

602+
writeln!(w, "{INDENT}coverage body span: {body_span:?}")?;
601603
for (id, expression) in expressions.iter_enumerated() {
602604
writeln!(w, "{INDENT}coverage {id:?} => {expression:?};")?;
603605
}

0 commit comments

Comments
 (0)