Skip to content

Commit 17c34c8

Browse files
authored
Unrolled build for rust-lang#134497
Rollup merge of rust-lang#134497 - Zalathar:spans, r=jieyouxu coverage: Store coverage source regions as `Span` until codegen (take 2) This is an attempt to re-land rust-lang#133418: > Historically, coverage spans were converted into line/column coordinates during the MIR instrumentation pass. > This PR moves that conversion step into codegen, so that coverage spans spend most of their time stored as Span instead. > In addition to being conceptually nicer, this also reduces the size of coverage mappings in MIR, because Span is smaller than 4x u32. That PR was reverted by rust-lang#133608, because in some circumstances not covered by our test suite we were emitting coverage metadata that was causing `llvm-cov` to exit with an error (rust-lang#133606). --- The implementation here is *mostly* the same, but adapted for subsequent changes in the relevant code (e.g. rust-lang#134163). I believe that the changes in rust-lang#134163 should be sufficient to prevent the problem that required the original PR to be reverted. But I haven't been able to reproduce the original breakage in a regression test, and the `llvm-cov` error message is extremely unhelpful, so I can't completely rule out the possibility of this breaking again. r? jieyouxu (reviewer of the original PR)
2 parents 9e136a3 + aced4dc commit 17c34c8

21 files changed

+299
-271
lines changed

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

+10-26
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId, SourceRegion};
2-
3-
use crate::coverageinfo::mapgen::LocalFileId;
1+
use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId};
42

53
/// Must match the layout of `LLVMRustCounterKind`.
64
#[derive(Copy, Clone, Debug)]
@@ -126,30 +124,16 @@ pub(crate) struct CoverageSpan {
126124
/// Local index into the function's local-to-global file ID table.
127125
/// The value at that index is itself an index into the coverage filename
128126
/// table in the CGU's `__llvm_covmap` section.
129-
file_id: u32,
127+
pub(crate) file_id: u32,
130128

131129
/// 1-based starting line of the source code span.
132-
start_line: u32,
130+
pub(crate) start_line: u32,
133131
/// 1-based starting column of the source code span.
134-
start_col: u32,
132+
pub(crate) start_col: u32,
135133
/// 1-based ending line of the source code span.
136-
end_line: u32,
134+
pub(crate) end_line: u32,
137135
/// 1-based ending column of the source code span. High bit must be unset.
138-
end_col: u32,
139-
}
140-
141-
impl CoverageSpan {
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;
148-
// Internally, LLVM uses the high bit of `end_col` to distinguish between
149-
// code regions and gap regions, so it can't be used by the column number.
150-
assert!(end_col & (1u32 << 31) == 0, "high bit of `end_col` must be unset: {end_col:#X}");
151-
Self { file_id, start_line, start_col, end_line, end_col }
152-
}
136+
pub(crate) end_col: u32,
153137
}
154138

155139
/// Holds tables of the various region types in one struct.
@@ -184,15 +168,15 @@ impl Regions {
184168
#[derive(Clone, Debug)]
185169
#[repr(C)]
186170
pub(crate) struct CodeRegion {
187-
pub(crate) span: CoverageSpan,
171+
pub(crate) cov_span: CoverageSpan,
188172
pub(crate) counter: Counter,
189173
}
190174

191175
/// Must match the layout of `LLVMRustCoverageBranchRegion`.
192176
#[derive(Clone, Debug)]
193177
#[repr(C)]
194178
pub(crate) struct BranchRegion {
195-
pub(crate) span: CoverageSpan,
179+
pub(crate) cov_span: CoverageSpan,
196180
pub(crate) true_counter: Counter,
197181
pub(crate) false_counter: Counter,
198182
}
@@ -201,7 +185,7 @@ pub(crate) struct BranchRegion {
201185
#[derive(Clone, Debug)]
202186
#[repr(C)]
203187
pub(crate) struct MCDCBranchRegion {
204-
pub(crate) span: CoverageSpan,
188+
pub(crate) cov_span: CoverageSpan,
205189
pub(crate) true_counter: Counter,
206190
pub(crate) false_counter: Counter,
207191
pub(crate) mcdc_branch_params: mcdc::BranchParameters,
@@ -211,6 +195,6 @@ pub(crate) struct MCDCBranchRegion {
211195
#[derive(Clone, Debug)]
212196
#[repr(C)]
213197
pub(crate) struct MCDCDecisionRegion {
214-
pub(crate) span: CoverageSpan,
198+
pub(crate) cov_span: CoverageSpan,
215199
pub(crate) mcdc_decision_params: mcdc::DecisionParameters,
216200
}

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

+2-3
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,10 @@ pub(crate) fn create_pgo_func_name_var<'ll>(
4040
}
4141
}
4242

43-
pub(crate) fn write_filenames_to_buffer<'a>(
44-
filenames: impl IntoIterator<Item = &'a str>,
45-
) -> Vec<u8> {
43+
pub(crate) fn write_filenames_to_buffer(filenames: &[impl AsRef<str>]) -> Vec<u8> {
4644
let (pointers, lengths) = filenames
4745
.into_iter()
46+
.map(AsRef::as_ref)
4847
.map(|s: &str| (s.as_c_char_ptr(), s.len()))
4948
.unzip::<_, _, Vec<_>, Vec<_>>();
5049

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

+30-30
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
use std::iter;
1+
use std::sync::Arc;
22

33
use itertools::Itertools;
44
use rustc_abi::Align;
55
use rustc_codegen_ssa::traits::{
66
BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
77
};
8-
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
8+
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
99
use rustc_hir::def_id::{DefId, LocalDefId};
1010
use rustc_index::IndexVec;
1111
use rustc_middle::mir;
1212
use rustc_middle::ty::{self, TyCtxt};
1313
use rustc_session::RemapFileNameExt;
1414
use rustc_session::config::RemapPathScopeComponents;
1515
use rustc_span::def_id::DefIdSet;
16-
use rustc_span::{Span, Symbol};
16+
use rustc_span::{SourceFile, StableSourceFileId};
1717
use tracing::debug;
1818

1919
use crate::common::CodegenCx;
@@ -22,6 +22,7 @@ use crate::coverageinfo::mapgen::covfun::prepare_covfun_record;
2222
use crate::llvm;
2323

2424
mod covfun;
25+
mod spans;
2526

2627
/// Generates and exports the coverage map, which is embedded in special
2728
/// linker sections in the final binary.
@@ -131,45 +132,51 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
131132
generate_covmap_record(cx, covmap_version, &filenames_buffer);
132133
}
133134

134-
/// Maps "global" (per-CGU) file ID numbers to their underlying filenames.
135+
/// Maps "global" (per-CGU) file ID numbers to their underlying source files.
135136
struct GlobalFileTable {
136-
/// This "raw" table doesn't include the working dir, so a filename's
137+
/// This "raw" table doesn't include the working dir, so a file's
137138
/// global ID is its index in this set **plus one**.
138-
raw_file_table: FxIndexSet<Symbol>,
139+
raw_file_table: FxIndexMap<StableSourceFileId, Arc<SourceFile>>,
139140
}
140141

141142
impl GlobalFileTable {
142143
fn new() -> Self {
143-
Self { raw_file_table: FxIndexSet::default() }
144+
Self { raw_file_table: FxIndexMap::default() }
144145
}
145146

146-
fn global_file_id_for_file_name(&mut self, file_name: Symbol) -> GlobalFileId {
147+
fn global_file_id_for_file(&mut self, file: &Arc<SourceFile>) -> GlobalFileId {
147148
// Ensure the given file has a table entry, and get its index.
148-
let (raw_id, _) = self.raw_file_table.insert_full(file_name);
149+
let entry = self.raw_file_table.entry(file.stable_id);
150+
let raw_id = entry.index();
151+
entry.or_insert_with(|| Arc::clone(file));
152+
149153
// The raw file table doesn't include an entry for the working dir
150154
// (which has ID 0), so add 1 to get the correct ID.
151155
GlobalFileId::from_usize(raw_id + 1)
152156
}
153157

154158
fn make_filenames_buffer(&self, tcx: TyCtxt<'_>) -> Vec<u8> {
159+
let mut table = Vec::with_capacity(self.raw_file_table.len() + 1);
160+
155161
// LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
156162
// requires setting the first filename to the compilation directory.
157163
// Since rustc generates coverage maps with relative paths, the
158164
// compilation directory can be combined with the relative paths
159165
// to get absolute paths, if needed.
160-
use rustc_session::RemapFileNameExt;
161-
use rustc_session::config::RemapPathScopeComponents;
162-
let working_dir: &str = &tcx
163-
.sess
164-
.opts
165-
.working_dir
166-
.for_scope(tcx.sess, RemapPathScopeComponents::MACRO)
167-
.to_string_lossy();
168-
169-
// Insert the working dir at index 0, before the other filenames.
170-
let filenames =
171-
iter::once(working_dir).chain(self.raw_file_table.iter().map(Symbol::as_str));
172-
llvm_cov::write_filenames_to_buffer(filenames)
166+
table.push(
167+
tcx.sess
168+
.opts
169+
.working_dir
170+
.for_scope(tcx.sess, RemapPathScopeComponents::MACRO)
171+
.to_string_lossy(),
172+
);
173+
174+
// Add the regular entries after the base directory.
175+
table.extend(self.raw_file_table.values().map(|file| {
176+
file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy()
177+
}));
178+
179+
llvm_cov::write_filenames_to_buffer(&table)
173180
}
174181
}
175182

@@ -182,7 +189,7 @@ rustc_index::newtype_index! {
182189
/// An index into a function's list of global file IDs. That underlying list
183190
/// of local-to-global mappings will be embedded in the function's record in
184191
/// the `__llvm_covfun` linker section.
185-
pub(crate) struct LocalFileId {}
192+
struct LocalFileId {}
186193
}
187194

188195
/// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU)
@@ -208,13 +215,6 @@ impl VirtualFileMapping {
208215
}
209216
}
210217

211-
fn span_file_name(tcx: TyCtxt<'_>, span: Span) -> Symbol {
212-
let source_file = tcx.sess.source_map().lookup_source_file(span.lo());
213-
let name =
214-
source_file.name.for_scope(tcx.sess, RemapPathScopeComponents::MACRO).to_string_lossy();
215-
Symbol::intern(&name)
216-
}
217-
218218
/// Generates the contents of the covmap record for this CGU, which mostly
219219
/// consists of a header and a list of filenames. The record is then stored
220220
/// as a global variable in the `__llvm_covmap` section.

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

+31-18
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,16 @@ use rustc_abi::Align;
1010
use rustc_codegen_ssa::traits::{
1111
BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
1212
};
13-
use rustc_middle::bug;
1413
use rustc_middle::mir::coverage::{
1514
CovTerm, CoverageIdsInfo, Expression, FunctionCoverageInfo, Mapping, MappingKind, Op,
1615
};
1716
use rustc_middle::ty::{Instance, TyCtxt};
17+
use rustc_span::Span;
1818
use rustc_target::spec::HasTargetSpec;
1919
use tracing::debug;
2020

2121
use crate::common::CodegenCx;
22-
use crate::coverageinfo::mapgen::{GlobalFileTable, VirtualFileMapping, span_file_name};
22+
use crate::coverageinfo::mapgen::{GlobalFileTable, VirtualFileMapping, spans};
2323
use crate::coverageinfo::{ffi, llvm_cov};
2424
use crate::llvm;
2525

@@ -67,12 +67,8 @@ pub(crate) fn prepare_covfun_record<'tcx>(
6767
fill_region_tables(tcx, global_file_table, fn_cov_info, ids_info, &mut covfun);
6868

6969
if covfun.regions.has_no_regions() {
70-
if covfun.is_used {
71-
bug!("a used function should have had coverage mapping data but did not: {covfun:?}");
72-
} else {
73-
debug!(?covfun, "unused function had no coverage mapping data");
74-
return None;
75-
}
70+
debug!(?covfun, "function has no mappings to embed; skipping");
71+
return None;
7672
}
7773

7874
Some(covfun)
@@ -121,49 +117,66 @@ fn fill_region_tables<'tcx>(
121117
covfun: &mut CovfunRecord<'tcx>,
122118
) {
123119
// Currently a function's mappings must all be in the same file as its body span.
124-
let file_name = span_file_name(tcx, fn_cov_info.body_span);
120+
let source_map = tcx.sess.source_map();
121+
let source_file = source_map.lookup_source_file(fn_cov_info.body_span.lo());
125122

126-
// Look up the global file ID for that filename.
127-
let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
123+
// Look up the global file ID for that file.
124+
let global_file_id = global_file_table.global_file_id_for_file(&source_file);
128125

129126
// Associate that global file ID with a local file ID for this function.
130127
let local_file_id = covfun.virtual_file_mapping.local_id_for_global(global_file_id);
131-
debug!(" file id: {local_file_id:?} => {global_file_id:?} = '{file_name:?}'");
132128

133129
let ffi::Regions { code_regions, branch_regions, mcdc_branch_regions, mcdc_decision_regions } =
134130
&mut covfun.regions;
135131

132+
let make_cov_span = |span: Span| {
133+
spans::make_coverage_span(local_file_id, source_map, fn_cov_info, &source_file, span)
134+
};
135+
let discard_all = tcx.sess.coverage_discard_all_spans_in_codegen();
136+
136137
// For each counter/region pair in this function+file, convert it to a
137138
// form suitable for FFI.
138139
let is_zero_term = |term| !covfun.is_used || ids_info.is_zero_term(term);
139-
for Mapping { kind, ref source_region } in &fn_cov_info.mappings {
140+
for &Mapping { ref kind, span } in &fn_cov_info.mappings {
140141
// If the mapping refers to counters/expressions that were removed by
141142
// MIR opts, replace those occurrences with zero.
142143
let kind = kind.map_terms(|term| if is_zero_term(term) { CovTerm::Zero } else { term });
143144

144-
let span = ffi::CoverageSpan::from_source_region(local_file_id, source_region);
145+
// Convert the `Span` into coordinates that we can pass to LLVM, or
146+
// discard the span if conversion fails. In rare, cases _all_ of a
147+
// function's spans are discarded, and the rest of coverage codegen
148+
// needs to handle that gracefully to avoid a repeat of #133606.
149+
// We don't have a good test case for triggering that organically, so
150+
// instead we set `-Zcoverage-options=discard-all-spans-in-codegen`
151+
// to force it to occur.
152+
let Some(cov_span) = make_cov_span(span) else { continue };
153+
if discard_all {
154+
continue;
155+
}
156+
145157
match kind {
146158
MappingKind::Code(term) => {
147-
code_regions.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) });
159+
code_regions
160+
.push(ffi::CodeRegion { cov_span, counter: ffi::Counter::from_term(term) });
148161
}
149162
MappingKind::Branch { true_term, false_term } => {
150163
branch_regions.push(ffi::BranchRegion {
151-
span,
164+
cov_span,
152165
true_counter: ffi::Counter::from_term(true_term),
153166
false_counter: ffi::Counter::from_term(false_term),
154167
});
155168
}
156169
MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => {
157170
mcdc_branch_regions.push(ffi::MCDCBranchRegion {
158-
span,
171+
cov_span,
159172
true_counter: ffi::Counter::from_term(true_term),
160173
false_counter: ffi::Counter::from_term(false_term),
161174
mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params),
162175
});
163176
}
164177
MappingKind::MCDCDecision(mcdc_decision_params) => {
165178
mcdc_decision_regions.push(ffi::MCDCDecisionRegion {
166-
span,
179+
cov_span,
167180
mcdc_decision_params: ffi::mcdc::DecisionParameters::from(mcdc_decision_params),
168181
});
169182
}

0 commit comments

Comments
 (0)